TCG-ORACLE-GO v0.1 — the gopls oracle process model
01Status: v0.1 — authored with GO-AI-NATIVE-PLAN v0.1 (Phase 3), implemented by its Phase 7.
02The component brief is
tools/vibe-agentic-tcg-go.xml; the
message grammar is
TCG-PROTOCOL-GO-v0.1.
03This document owns the oracle PROCESS: resolution, LSP lifecycle, configuration, overlays, quiescence, the fidelity posture, and latency.
04Where the sibling Rust
mechanism cites measured spike facts, this one names the same
quantities as campaign-measured — the bench harness
(go-ai-native-tcg bench) is the instrument; no Go corpus or baseline
has yet been taken, so the figures below are posted targets rather
than measured ones. Creating the Go test codebase to measure against
is deliberately far-future work (the corpus could be LLM-generated or
fuzzer-generated); it is not being built now — a target moves only
with a committed REPORT reason.
1. The process and its resolution
05req r1
06The oracle process is the CONSUMER's own gopls binary — the stack
never bundles, links, or vendors an analyzer.
07Resolution order, run
from the project root so go.work/module context is honoured, each
failure recipe-carrying and never silently skipped:
- 08the
GO_AI_NATIVE_GOPLSenv override when set (a set-but-not-a-file value refuses with the recipe, without probing further), thengoplson PATH; $GOBIN/gopls, then$(go env GOBIN)/gopls;$(go env GOPATH)/bin/gopls;- hard failure: the bridge's
gopls-missingerror with the recipego install golang.org/x/tools/gopls@latest.
09Installing this stack OBLIGES the machine to carry go ≥ 1.24 and gopls (the same posture as rust-analyzer for the Rust stack and node ≥ 22.6 for the TS one): inside the stack's own test suite an absent tool is a recipe-carrying FAILURE, never a skip; outside the stack no obligation exists.
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 gopls_version
(crates/go-ai-native-tcg/src/serve.rs:74-84), beside position_encoding,
pull_diagnostics and ready. The path half does not: the path IS resolved —
resolve_gopls (crates/go-ai-native-tcg-bridge/src/lib.rs:145) returns it —
but it is never put into the result. The Rust stack states the same fact and
fails it the same way.
2. LSP session and capabilities
11req r1
12The bridge speaks LSP 3.17 over the child's stdio (Content-Length framing).
13The initialize request declares:
- 14utf-8 in
general.positionEncodings(fallback: utf-16 positions converted through the line's text, unit-tested on non-ASCII content), - pull diagnostics (
textDocument.diagnostic), - publish-diagnostics handling,
- and
window.workDoneProgress.
15Every downstream feature keys off the GRANTED set — a capability the server does 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).
17Diagnostics channel, stated honestly. gopls has historically
PUSHED diagnostics (textDocument/publishDiagnostics) and gained pull
support later than rust-analyzer; which channel the shipped gopls
grants is pinned by the Phase-7 live chain and recorded in the
differential corpus. Specified, not built — the history is true, the
recording is not. The channel IS negotiated at run time and carried on the
capabilities (pull_diagnostics,
crates/go-ai-native-tcg-bridge/src/client.rs:33), but nothing records
which one the shipped gopls granted: research/tcg-bench/ holds a
TypeScript corpus (corpus/) and a Rust one (corpus-rust/) and no Go
corpus at all, so there is no differential corpus for this to be recorded
in.
18The bridge supports BOTH: prefer the pull channel
when granted; otherwise collect pushed diagnostics for the target
document with a bounded settle window after didOpen/didChange.
19Either way validate answers one document's diagnostics — never a
whole-workspace sweep.
3. Configuration
20req r1
21The bridge ships one configuration object, passed as
initializationOptions and repeated in every
workspace/configuration answer.
22v0.1 keeps it minimal and DOCUMENTED — gopls's defaults are production-grade (its diagnostics are not gated behind experimental flags the way rust-analyzer's E0308-class ones are; the Rust bridge's config lesson transfers as a posture, not as content):
- 23staticcheck integration stays OFF (the floor runs staticcheck itself; one tool, one truth),
- analyses stay at gopls defaults,
- and any future knob (build tags, env) extends this one object in one place.
4. Overlays and versions
24req r1
25An overlay is an LSP-owned text document:
- 26
didOpen {uri, version: 1, text}claims the document (the server stops reading disk for it), didChangewith full-text sync and a MONOTONICALLY increasing per-document version replaces it,didClosereleases it back to disk.
27The rules the TS and Rust campaigns proved are law here and the bridge enforces them structurally:
- 28versions never repeat within an
overlay's lifetime (a monotonic counter per open document, never derived
from content); clearing an overlay (
update {content: null}) closes the document and a later reopen starts again at 1 —crates/go-ai-native-tcg-bridge/src/oracle.rs, and the bridge's ownoverlay_versions_are_monotonic_and_close_resetstest; 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 file in
an existing package participates via
didOpenalone; update {content: null}maps todidClose.
5. The fidelity posture (gopls is go/types, not the compiler)
29req r1
30The three stacks now span a fidelity spectrum, and this oracle's place on it is spec, not fine print:
- 31The TS oracle IS the compiler (the LanguageService is tsc's engine — agreement by construction).
- rust-analyzer is NOT rustc (an independent, deliberately partial analysis).
- gopls stands on
go/types— the reference library implementation of the Go specification, the same frameworkgo vetbuilds on — while the gc compiler type-checks withtypes2, go/types' deliberately-synchronized port. The delta is a maintained-identical pair whose divergences are treated as bugs upstream: far tighter than rust-analyzer↔rustc, still not identity.
32Consequences, all normative:
- 33A clean
validatedoes NOT certify a clean floor. The floor (go-ai-native floor→ gofmt/vet/build/test) remains the truth; consumer-facing docs repeat it. - The differential corpus curates diagnostic classes and pins each to
the floor's own verdict (
go build/go vetexit + message class) through a committed mapping table: type mismatch, undeclared name, wrong argument count, unknown field, missing return, unused import/variable. Specified, not built: there is no Go corpus and no mapping table. The corpora that exist areresearch/tcg-bench/corpus/(TypeScript, 7 cases) andresearch/tcg-bench/corpus-rust/(Rust, 9 cases); nocorpus-goexists anywhere in the tree. The six classes named above are a curation nobody has performed yet. - Known asymmetries are DOCUMENTED-GAP corpus cases, not omissions —
the standing candidates to probe in Phase 7: diagnostics gated on
saved-vs-overlay state,
go.mod-dependent resolution under a pure overlay, and vet-only findings (printf shapes) that the floor reports and the oracle may not. Each observed asymmetry becomes a corpus case asserting exactly that shape, so the gap list never rots.
6. Quiescence, degradation, never crashes
34req r1
35After initialized, the server loads the workspace (go.mod parsing,
package metadata, cache priming).
36The bridge bounds its readiness wait
by a deadline keyed on workDoneProgress end events for the initial
load; a deadline pass degrades: answers carry degraded: true, so
callers can distinguish warm truth from cold best-effort.
37The Rust campaign's falsified progress-drain heuristic is inherited as a WARNING, not a mechanism: no wait strategy is trusted until the Phase-7 live chain pins gopls's actual signalling, and a replay test pins whatever is chosen.
38B5 extends to the whole session:
- 39an 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
40req r1
41One long-lived child per (root, session).
42Graceful exit is the LSP
dance — shutdown request, exit notification — with kill-on-drop as
the backstop; the no-zombie property is test-asserted.
43Paths become
URIs only after verbatim-prefix stripping (\\?\ breaks child argv
and URI builders — the standing house lesson).
44stdout carries LSP frames only; gopls's own stderr chatter is drained and discarded by the reader (surfaced only in bridge debug logging), so protocol streams stay clean.
8. Latency posture
45req r1
46Targets are POSTED and MEASURED, never CI-gated (the standing split: gate what cannot flake, record what can).
47Posted targets for demo-class trees:
- 48warm
validatep50 < 500 ms, completep50 < 300 ms — posted, not yet measured: the bench harness (crates/go-ai-native-tcg/src/bench.rs) records per-casewarm_msforvalidateonly and computes no percentile of any kind; the measurement corpus is deliberately far-future work (BACKLOG.mdB-042),- cold init-to-ready < 15 s.
49The bench harness
(go-ai-native-tcg bench) records distributions per run; the Phase-7
ledger entry carries the first measured set on research/go-demo,
and a target that moves, moves in a committed REPORT with a reason.
Specified, not built — the harness, not the ledger entry. The harness
ships and does record a per-case distribution: run_bench
(crates/go-ai-native-tcg/src/bench.rs:106) collects warm_ms over three
warm passes per case. What does not exist is the measured set:
research/tcg-bench/reports/ holds bench-2026-07-07-baseline.json
(TypeScript) and bench-rust-2026-07-07-baseline.json (Rust) and no Go
run at all, so nothing has ever been measured on research/go-demo and
there is no committed REPORT for a target to move against.
50Large-workspace consumers are warned about the product's 60 s
first-request ceiling; the relay's eager init at serve start spends
the cold cost as early as possible. Specified, not built — the eager
init is real, the 60 s ceiling is not a number this stack has anywhere.
The relay does spend the cold cost up front: serve boots gopls before
the first host frame (crates/go-ai-native-tcg/src/serve.rs:235). A
large-workspace warning also exists, one document over — the tcg brief's
##RISK-COLD-INIT-ON-LARGE-WORKSPACES — but it is a spec-layer risk
record, not anything the product emits to a consumer at run time. And the
60 s figure is supported by nothing: the shipped readiness budget is
READINESS_BUDGET = 45 s (crates/go-ai-native-tcg/src/lib.rs:32) and
this document's own ##TARGET-COLD-INIT posts < 15 s. Three numbers,
and 60 is not one of them.