Core
The core models describe source files and the facts providers return about them. The reference links their generated schemas.
Paths and files
ProjectPath is a normalized UTF-8 path relative
to the workspace root. It uses /, contains no . or .. component, and cannot name .rift,
where the server stores its own state. Normalized means Unicode NFC.
The index skips symlinks. A linked file answers under its own path when the source policy includes it.
Source locations
TextRange is a pair of half-open UTF-8 byte offsets.
Each provider converts its toolchain's positions to byte offsets, so ranges from different
providers use one scale.
nodes returns the syntax nodes covering one byte position,
outermost first. Each node carries its grammar kind and byte range.
Source discovery
Source resolvers run before syntax or semantic analysis. They inspect workspace files, manifests, and lockfiles, then publish a source catalog of the project's source units and the packages the project depends on. A resolver reads no package source, downloads no package, and does not change the environment.
SourceUnitId names one unit in that catalog,
and a package file the global index answers from takes the same form:
rift://source/cargo/crates.io/demo@1.0.0/src/lib.rs. This package address names its manager,
registry, package and exact served version, then the physical path in that release. A project
source address uses rift://source/project/ followed by its project-relative path. Logical
symbol identities remain separate from these physical addresses.
The key is never a digest. A Digest reaches the
wire only as a short witness where byte identity matters: the first eight hex characters of a
SHA-256, the same convention NodeId's witness uses. The server computes and compares the full
digest internally, so it never travels whole.
SourceLocationKind names where a
declaration's source belongs: project, dependency, standard library, or external source.
SourceKind names how the source came to exist:
authored, generated, or synthetic for a declaration with no source unit. The two stay separate
because generated code can belong to the project or to a dependency.
A declaration in that package file carries both in its
SymbolOrigin, beside the package that owns
it:
{
"location": "dependency",
"package": {
"manager": "cargo",
"registry": "crates.io",
"name": "demo",
"version": "1.0.0"
},
"source_kind": "authored"
}A resolver publishes a new catalog revision when files, manifests, lockfiles, version pins, or
generated-source metadata change, and fact providers publish a new revision after they analyze
one. A read whose scope reaches packages reports a resolver that read less than the workspace
states as a package_context_degraded
ReadWarning, naming the resolver by its
manager name, such as cargo or npm.
Semantic facts
Providers return facts in families: symbols, the syntax nodes that write them, relationships
between symbols, resolved types, diagnostics, and history. A family no provider can produce
refuses with capability_unavailable. Signatures and documentation travel on the symbol itself,
covered under the types family.
A search result carries the answer, pagination, and warnings. Exact symbol and source
reads carry an outcome and immutable view; a symbol read can carry a declaration_cursor. A condition the caller must
weigh - an index that lags the tree - arrives as a typed entry in warnings; the
Reads section in the MCP reference states the paging and warning
contract.
SymbolOrigin keeps source location separate from
source kind. A synthetic symbol carries neither a location nor a package; dependency and
standard-library symbols can carry facts even when their provider cannot return source bytes.
The declaration's own source-catalog unit, where its location differs from the project, rides on
a get_symbol declaration binding, addressed by unit instead
of the path a project declaration carries. The logical symbol identity remains separate from
that physical source address.
History facts
The history family is resolved from the workspace's version-control history rather than from
the current tree. A SymbolHistory is one
symbol's timeline - each SymbolVersion names
the RevisionId that touched it and what the
revision did: introduced it, changed its body, changed its signature, moved it, removed it.
The history provider correlates declarations across revisions using their parsed structure and
surrounding changes. A SymbolId can change when a
language includes a module path in its qualified name; the timeline still records the move.
The walk is bounded by the configured history depth, and the timeline rides a
get_symbol result when the request asks for history.