ADR-0005: Absence-based diagnostics gate on a loader-asserted complete workspace
- Status: Accepted
- Date: 2026-07-02
Context
The analyzer's "Unknown seam" deliberately silences every unresolved name so that cross-file
symbols the host has not loaded never false-flag. The cost: calling a genuinely undeclared
function (usseState(0)) produces zero diagnostics — Godot itself errors on it, and the
.guitkx LSP built on this analyzer cannot catch obvious typos live.
Reporting "defined nowhere" is an absence proof: it requires seeing everywhere a definition could live. No signal the database already had can establish that:
source_root().is_some()is true after one lone file is opened;project_config().is_some()is true for a single-file CLI run (theproject.godotwalk-up discovery finds it) while only one file is loaded;- per-name registry hits (
global_registry) prove presence, never absence.
Options considered: (a) emit whenever a project.godot is present (unsound — the single-file
case above false-flags every cross-file class_name); (b) heuristics on the name shape (rejected
outright — fragile); (c) an explicit, loader-owned completeness assertion.
Decision
We will add a complete: bool field on the SourceRoot salsa input — a claim only the
loader can truthfully make — plumbed as Change::set_workspace_complete /
setWorkspaceComplete through the session and the napi/wasm bindings, and gate the new
UNDEFINED_FUNCTION / UNDEFINED_IDENTIFIER codes (ERROR-default: they are compile errors in
Godot) on it plus per-emission guards: a top-level script class, a fully engine-native base
chain, a project engine version not newer than the bundled model, and a per-name miss of every
resolution tier (locals, members, engine base, engine globals, class_name registry, autoload
registry).
The CLI earns the claim by loading the whole project root as context (targets keep exclusive
reporting) with a Godot-faithful walk — .gitignore deliberately not honored, .gdignore
treated as Godot's directory marker, dot-directories skipped — and withholds it whenever any
filesystem target resolves to a different project root, stdin is involved, any file fails to
read, or the project contains .gdextension/C# sources (runtime-registered classes are
invisible to the analyzer).
Rejected alternative: project-config presence as the gate — demonstrated unsound on the single-file invocation; validated instead against all 138 godot-demo-projects (216 first-run false positives driven to 0 by root-cause fixes, none by weakening the gate).
Consequences
Easier: the guitkx LSP (which feeds the whole project) can arm live undefined-symbol detection by
one call; any host that cannot honestly claim completeness gets exactly the old silent-seam
behavior — soundness by default. Harder: the claim is trust-based — a host that lies gets false
positives (documented on every plumbing surface); deep CLI loads read every project .gd even
for one target (the documented load→fan-out design, and per-file commands use a shallow load);
and the bundled engine model must track the latest stable Godot or newer-engine projects are
gated off (multi-version bundling is the standing GODOT-SYNC plan).