TCG-ORACLE-RUST v0.1 — the rust-analyzer oracle process model
01Status: v0.1 — authored with AGENTIC-TCG-RUST-PLAN v0.1 (Phase 1), implemented by its Phases 3–4.
02The component brief is
tools/vibe-agentic-tcg-rust.xml;
the message grammar is
TCG-PROTOCOL-RUST-v0.1.
03This document owns the oracle PROCESS: resolution, LSP lifecycle, configuration, overlays, quiescence, the approximation posture, and latency.
04Spike facts cited here were measured against rust-analyzer 1.93.1 on 2026-07-07 (AGENTIC-TCG-RUST-PLAN Phase 0).
1. The process and its resolution
05req r1
06The oracle process is the CONSUMER's own rust-analyzer binary — the
stack never bundles, links, or vendors an analyzer.
07Resolution order,
run from the project root so rust-toolchain.toml pinning is
honoured, each failure recipe-carrying and never silently skipped:
- 08
rustup which rust-analyzer(the toolchain's component); rust-analyzeron PATH;- hard failure: the bridge's
rust-analyzer-missingerror with the reciperustup component add rust-analyzer.
09Installing this stack OBLIGES the machine to carry rust-analyzer (the same posture as node ≥ 22.6 for the TS stack): inside the stack's own test suite an absent analyzer is a recipe-carrying FAILURE, never a skip; outside the stack no obligation exists — a project without rust-ai-native gets the product's not-installed recipe and owes nothing.
10The resolved path and the server's reported version land in
the init result. Specified, not built — half of it. The version half ships:
init_result emits ra_version (crates/rust-ai-native-tcg/src/serve.rs:76-86),
beside position_encoding, pull_diagnostics and quiescent. The path half
does not: the path IS resolved — resolve_rust_analyzer
(crates/rust-ai-native-tcg-bridge/src/lib.rs:146) returns it — but it is never
put into the result, and ra_path occurs in this package only as the shape
TCG-PROTOCOL-RUST-v0.1.xml#OP-INIT promises, never as a field the code emits.
2. LSP session and capabilities
11req r2
12The bridge speaks LSP 3.17 over the child's stdio (Content-Length framing).
13The initialize request declares:
- 14utf-8 in
general.positionEncodings(granted by 1.93.1 — positions then need line-base conversion only; the utf-16 fallback converts through the line's text and is unit-tested on non-ASCII content), - pull diagnostics
(
textDocument.diagnostic), window.workDoneProgress,- and the
experimental
serverStatusNotification.
15Every downstream feature keys off the GRANTED set — a capability the server did not grant degrades per §6 into a well-formed error or a documented fallback, never a crash.
16The bridge answers the server's own requests:
workspace/configuration (with §3's config object),
window/workDoneProgress/create and client/registerCapability (null
results).
3. Configuration: experimental diagnostics, deliberately on
17req r3
18The spike's central finding: rust-analyzer's most valuable native
diagnostics — type-mismatch (E0308), unresolved-name (E0425) — sit
behind the DEFAULT-OFF diagnostics.experimental.enable.
19A null-config oracle answers silence for the very classes the tool exists to catch.
20The bridge therefore ships one config object —
{"diagnostics": {"experimental": {"enable": true}}} — passed BOTH as
initializationOptions and as every workspace/configuration answer.
21This is a deliberate, documented posture: the enabled set is experimental by rust-analyzer's own naming, which is one more reason §5's approximation statement is spec, not fine print.
22Future config needs (feature flags, cargo target selection) extend this object in one place.
4. Overlays and versions
23req r4
24An overlay is an LSP-owned text document: didOpen {uri, version: 1,
text} claims the document (the server stops reading disk for it),
didChange with full-text sync and a MONOTONICALLY increasing
per-document version replaces it, didClose releases it back to disk.
25The rules the TS campaign learned the hard way are LSP-native law here and the bridge enforces them structurally:
- 26versions never repeat within an
overlay's lifetime (a monotonic counter per open document, never derived
from content); clearing an overlay closes the document and a later
reopen starts again at 1 —
crates/rust-ai-native-tcg-bridge/src/oracle.rs:184(docs.remove); validateWITHOUT inline content reads the disk file and opens it with that text, so version bookkeeping has exactly one owner (the bridge) and a later disk edit is picked up by the next validate'sdidChange;- an overlaid file need not exist on disk — a hypothetical new module
participates via
didOpenalone (spike-proven: a seeded error in a pure overlay is diagnosed with zero disk writes); update {content: null}maps todidClose.
5. The approximation posture (r-a is not rustc)
27req r5
28The TS oracle answers with tsc's own engine; THIS oracle answers with rust-analyzer's independent analysis, which is deliberately partial.
29Consequences, all normative:
- 30A clean
validatedoes NOT certify a clean floor. The floor (rust-ai-native floor— the seven steps: cargo fmt → cargo test → clippy → conform → specmap → test-gate → fast-loop; the compile rides inside the test step) remains the truth; consumer-facing docs repeat it. - The differential corpus curates classes INSIDE r-a's native competence; each class is pinned to cargo check through the committed mapping table (1.93.1 rows: E0308↔E0308, E0425↔E0425, E0107↔E0061 arity, E0559↔E0609 unknown-field, E0063↔E0063, E0599↔E0599). Diagnostic CODES may differ for the same defect; existence-grain agreement is the claim, through the table.
- Known silences are DOCUMENTED-GAP corpus cases, not omissions: privacy at 1.93.1 is the standing exhibit — the oracle answers nothing while cargo check speaks (rustc's code depends on the reference shape: E0423 for a use-imported tuple constructor, E0603 for the module-path form — one defect class, two codes; the corpus pins the E0423 shape). The case asserts exactly that asymmetry so a future r-a flips it red and the gap list never rots.
- Borrow-check subtleties, trait-solver edges, and macro-heavy code are named as the open delta class; no corpus case claims them.
6. Quiescence, degradation, never crashes
31req r6
32After initialized, the server loads the workspace (cargo metadata,
cache priming).
33The bridge waits for experimental/serverStatus with
quiescent: true, bounded by a deadline — and that flag is the ONLY
trusted signal.
34Two live-chain findings harden this (2026-07-07, Phase 3):
- 35(a) rust-analyzer does NOT echo
serverStatusNotificationin its InitializeResult even though it honours the declared client capability, so there is nothing to key a capability check off — the bridge declares and trusts the channel; - (b) a progress-drain heuristic ("initial workDoneProgress tokens ended") was tried and FALSIFIED twice — a fast first token drains while indexing continues, yielding confident empty answers — so it is deliberately ABSENT, and a replay test pins that progress noise never satisfies the wait.
36A
deadline pass degrades: answers carry degraded: true, so callers
can distinguish warm truth from cold best-effort.
37B5 extends to the whole session:
- 38an op the relay does not know answers a protocol error naming the known set;
- an analyzer crash surfaces
oracle-crashedop-grain and ends the session (the product registry owns respawn-once); - no input may poison the session.
7. Process lifecycle and Windows discipline
39req r7
40One long-lived child per (root, session).
41Graceful exit is the LSP
dance — shutdown request, exit notification — with kill-on-drop as
the backstop; the no-zombie property is test-asserted (spike-proven:
clean exit code 0, no surviving pid). Built 2026-08-05 — the proof caught up
to the mechanism, which had shipped long before it. The dance ships
(shutdown at crates/rust-ai-native-tcg-bridge/src/oracle.rs:356) and so
does the backstop (Drop for ChildTransport → kill() then wait(),
client.rs:346-350). The assertion now ships too:
dropping_the_oracle_kills_the_child_process_no_zombie
(tests/live_oracle.rs) reads the child PID through
ChildTransport::child_pid(), proves that process is alive, drops the wrapper
— deliberately NOT the graceful path, so the kill-on-drop backstop is what is
exercised — and polls the OS process table until the PID is gone, treating a
recycled PID as dead by comparing start_time. Between 2026-08-04 and this
build the sentence was a promise; it is now a description.
42Paths become URIs only after
verbatim-prefix stripping (\\?\ breaks child argv and URI builders —
the standing lesson's fourth home).
43stdout carries LSP frames only;
rust-analyzer's own stderr chatter is drained and discarded by the
reader (surfaced only in bridge debug logging), so protocol streams
stay clean. Specified, not built — the conclusion holds, the mechanism
described does not. Protocol streams do stay clean, but not by draining:
the child is spawned .stderr(std::process::Stdio::null())
(crates/rust-ai-native-tcg-bridge/src/client.rs:303, and again at
lib.rs:163), so the OS discards the chatter at the pipe and no reader
ever sees it. There is no bridge debug logging to surface it in — the
bridge crate has no logging facility at all.
8. Latency posture
44req r8
45Targets are POSTED and MEASURED, never CI-gated (the standing split: gate what cannot flake, record what can).
46Spike facts on a minimal crate, this box:
- 47init handshake ~10 ms;
- init-to-quiescent 14.7 s cache-COLD (sysroot indexing dominates) and 2.5 s warm;
- warm pull diagnostics 1–2 ms;
- hover ~1 ms;
- completion ~19 ms at 118 entries.
48Posted targets for demo-class trees:
- 49warm
validatep50 < 500 ms, completep50 < 300 ms — posted, not yet measured: the bench harness timesvalidateonly (crates/rust-ai-native-tcg/src/bench.rsemitscold_init_ms,validate_p50_ms,validate_p95_msand nocompletefield); the measurement corpus is deliberately far-future work (BACKLOG.mdB-042),- cold init-to-quiescent < 15 s.
50The bench harness records distributions per run; a target that moves, moves in a committed REPORT with a reason — and per the owner's resolution a miss CANCELS NOTHING: the campaign proceeds and the miss is reported prominently.
51Large-workspace consumers are warned —
the tcg brief's ##RISK-COLD-INIT-ON-LARGE-WORKSPACES carries the
spec-layer warning (14.7 s cache-cold on a minimal crate; a big consumer
tree may exceed the first-request budget) — and the relay's eager init at
serve start (before the host's first frame) spends the cold cost as
early as possible. The shipped ceiling is the 45 s quiescence budget
(QUIESCENCE_BUDGET, crates/rust-ai-native-tcg/src/lib.rs:33, used by
spawn_oracle and serve); this document's own
##TARGET-COLD-INIT-TO-QUIESCENT posts < 15 s for demo-class trees.