Files
trufflepig/docs/language-contract.md
Joshua MoonandClaude Opus 5.5 6d9e0113d4 fix(search): tighten lane D navigation per review
- map: extern block items (including Rust 2024 `unsafe extern`, which
  the pinned grammar parses as ERROR + block) carry the exact
  `extern block` container; map filters on it instead of a LIKE prefix
  that hid `mod externals` and `class ExternalApi` members.
- refs: collapse only same path, line, role and resolution; the count
  lives in a new `Hit.repeats` field (JSON `repeats`, lines `×N`), so
  `resolution` stays an enum. Test sites rank last whatever their role.
  Qualified refs pre-filter owners in SQL.
- show: import trails follow up to four hops with a visited set,
  resolve crate/self/super against the importing module (crate root =
  path before the last `src/`, plus enclosing inline mods), report the
  chain, and mark ties as `(1 of N)` with the tied declarations first
  in `also:`. Import tracing moves to search/import_trail.rs.
- Qualified lookups keep the pre-filter truncation flag.
- Workspace `search 'sym:'` ranks by the invocation directory; the
  directory is compared percent-encoded. Test scopes match any
  `tests` container segment.
- Workspace map misses count as no home hits; `no_indexed_path` is
  returned only when no member has a row.
- acquisition.rs splits into acquisition/{symbol,tests}.rs.
- Navigation contract moves to docs/navigation-contract.md with the
  JSON fields; language contract documents the impl-container limit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 09:26:37 -04:00

8.3 KiB

Language extraction and relationships

Shared evidence model

An observed occurrence records original source span, spelling, enclosing scope, and occurrence kind. A relationship separately records its target, state, source evidence, and resolver provenance. States distinguish statically resolved, candidate, and unresolved sites. An occurrence does not imply a resolved edge.

Only a documented static rule establishes resolution. Name-plus-arity and guessed receiver types remain candidates even with one candidate. Shadowing, duplicate declarations, conditional definitions, overloads, and missing module configuration preserve ambiguity. Tests cover successful local resolutions and false-resolution rejection; this is a local subset, not compiler-equivalent resolution across any of the supported languages.

Rust, TypeScript/JavaScript, and Luau use pinned Tree-sitter grammars with compiled reference queries. C# and PHP use pinned tree-sitter-c-sharp and tree-sitter-php grammars. Parsing has a 500 ms cancellation budget; syntax-tree depth is bounded at 256. Invalid UTF-8 leaves lexical indexing available but no structural facts, with invalid_encoding status. Parse errors retain recognized syntax observations with unresolved references rather than proving new targets.

Tree-sitter ABI compatibility is a supported range. Parser implements Sync, but parsing mutates it exclusively. Cancellation yields None, not an available partial tree; reset before the next independent parse. Tree-sitter API

Rust

Extraction records definitions, lexical containers, import syntax, local bindings, and references. Trait implementations and conditional definitions stay distinct. Local binding references and conservative unqualified direct calls resolve when lexical scope establishes their binding. Qualified names, imported targets, and receiver-dependent calls remain candidates or unresolved.

Rust module/import syntax does not constitute full workspace resolution. Macro expansion, trait selection, and type inference are outside this subset.

Items inside an impl block record its header as their container; items in an extern block record extern block. An impl container replaces enclosing inline-module containers, so sym:inner::Deep::go for mod inner { impl Deep { fn go() } } matches only when inner is also a file-path module component; sym:Deep::go always matches. sym:A::b qualification is textual: A must name the impl type or trait, a lexical container, or trailing module-path components derived from the file path (src, mod, lib, main dropped; - read as _). Qualifiers that only occur in order within that path match when no trailing match exists, which covers pub use re-exports. Import following reads the use clause spelling (a::b as c) and resolves crate:: to the path before the last src/; glob imports and paths through type aliases are not followed. Test-site ranking is by path convention and mod tests scopes (navigation contract); #[cfg(test)] on other items is not consulted.

The Codex skill's optional rustdoc workflow provides compiled item documentation and explicit trait/implementation links for one Cargo target and feature configuration. This evidence remains outside the search index and does not upgrade body occurrences to resolved relationships. Current source is verified separately before relying on generated spans.

C#

The .cs extension maps to csharp; cs and c# are query aliases. Extraction records namespaces, classes, interfaces, structs, records, enums, delegates, methods, constructors, properties, fields, locals, parameters, and identifier occurrences with original-byte spans. Razor .cshtml files remain text-searchable without C# structural extraction.

The pinned tree-sitter-c-sharp 0.23.5 grammar reports parse_error on BTCPayServer list/slice property patterns such as [.. { } multis]; recognized facts remain partial and references at those sites remain unresolved.

Lexical local and parameter references may resolve only when one visible binding is unique. Overloads, members, partial type merges, inheritance, qualified names, and receiver-dependent or method calls remain candidates or unresolved. The subset does not provide compiler-equivalent binding resolution.

PHP

.php and .phtml map to php and use pinned tree-sitter-php 0.24.2. Extraction records namespaces, classes, interfaces, traits, enums, functions, methods, properties, constants, enum cases, parameters, variables, imports, and identifier occurrences with original-byte spans. Inheritance and trait-use references remain candidate or unresolved evidence. The bounded PHP/XenForo resolver adds source-derived inheritance and literal parent::method() candidates without proving live XFCP state or general member dispatch. See the PHP and XenForo contract for exact limits and relationships.

TypeScript and JavaScript

Extraction includes functions, classes, fields, named arrow functions, overloads, imports, and local bindings. TypeScript covers .ts, .tsx, .mts, and .cts; JavaScript covers .js, .jsx, .mjs, and .cjs. Local resolution preserves shadowing and type/value distinctions where represented by the grammar. Functions assigned to static member properties are recorded; calls through those properties remain candidates because receiver binding is not inferred.

Only a unique explicit relative static ES source path, such as ./util.ts, can establish an imported module target. Extensionless paths and .js to .ts substitution remain candidates: ignored files and module-resolution settings can change the actual target. An imported module does not prove the target of every imported call, re-export, or runtime member.

Nearest tsconfig.json/jsconfig.json supports JSONC comments/trailing commas, compilerOptions.baseUrl, and single-wildcard paths as candidate evidence. Configuration with extends does not certify those mappings. Full Node/bundler resolution, package exports, and compiler project references are not implemented.

Luau

Extraction records functions, table methods, local bindings, type declarations, module export syntax, and require occurrences. Local bindings can resolve; require calls and module exports remain runtime candidate evidence. A spelling match does not prove that the builtin require is unshadowed or exports static.

String paths use relative candidates and nearest .luaurc aliases. An explicit root trufflepig.json selecting {"rojo_project":"default.project.json"} enables Rojo $path mapping candidates. Instance-path requires such as game.Service.Child stay candidates. Unconfigured instance names, dynamic exports, and runtime table mutation do not establish static targets.

DreamMaker

DreamMaker uses bounded comment/string-aware recovery over original bytes, not a complete grammar or preprocessor. File status is recovered or recovered_incomplete; occurrences carry dm-recovery provenance. The subset records absolute/nested declarations in indentation and brace forms, procs, verbs, locals, macros, includes, overrides, parent_type, and observed signals. Token/fact/candidate bounds can omit facts and are exposed through status or provenance. Unrecognized headers and macro-generated declarations are not a complete object tree. Byte recovery accepts non-UTF-8 source without shifting canonical offsets.

Same-file lexical bindings can resolve. A prior same-type proc override may resolve when the recovered file is complete and neither includes nor conditional ordering interfere. Inherited parent calls and project-wide include order remain unverified candidates, including explicit parent_type cases. Recovery does not infer semantic inheritance merely from lexical path ancestry. Upstream parent-proc implementation

SEND_SIGNAL, RegisterSignal, PROC_REF, and related spellings expose observed evidence; they do not prove a runtime call. Includes have candidate edges and conditional evidence without preprocessor evaluation. No dmdoc JSON sidecar is assumed or implemented. Upstream dmdoc entry point