Zenith Logic Foundry

Code Indexing

zlf can turn a repository into queryable code symbols, lexical documents, and graph relationships via Tree-sitter. The default code-index path emphasizes BM25 plus structural graph traversal and does not require vector embedding.

Supported languages

Java, C, C++, Python, Rust, JavaScript, TypeScript, Kotlin, Go, Swift — ten languages with pinned Tree-sitter grammars, versioned adapters, and golden parse fixtures.

How it works

The zlf-codeindex crate provides four layers:

  1. Tree-sitter ingestion — zero-config scan-root bootstrap, discovery, blob store, incremental per-file syntax extraction.
  2. Canonical code graph — stable content-derived identities, occurrence lowering, containment/call/import/extends edges, certainty provenance.
  3. Code retrieval — BM25 symbol search with identifier-boundary subtokenization, bounded graph traversal (code_search, code_callers, code_callees, code_path, code_cycle), and a shared CodeQuery AST for Prolog and JSON/HTTP.
  4. Benchmark harness — frozen corpus manifest, correctness oracles, incremental convergence, scale tiers (smoke/mid/target).

Rust API

use zlf_codeindex::{CodeGraph, CodeRetrieval, CodeQuery, IndexLifecycle, FileRef};

// 1. Open (or create) the lifecycle and graph.
let mut life = IndexLifecycle::open("my-index/index")?;
let graph   = CodeGraph::open("my-index")?;

// 2. Discover files.
let inventory = zlf_codeindex::discover("src/".as_ref(), &Default::default());

// 3. Ingest one file.
let f = FileRef {
    rel_path: "src/main.py".into(),
    language: Some(zlf_codeindex::Language::Python),
    size_bytes: 0,
};
life.import_file(&f, source, &[])?;

// 4. Sync into the graph.
graph.sync("my-repo", &life)?;

// 5. Query.
let retrieval = CodeRetrieval::new(graph.storage())?;
let hits      = retrieval.search("parse", 10, &Default::default(), 1000);
let callers   = retrieval.callers("code:sym:...", Default::default());

Prolog predicates

Once a repository is ingested, code symbols are ordinary graph facts queryable through the standard Prolog runtime:

% Label shortcut: every code_symbol is a fact.
? code_symbol(X).

% Property shortcut.
? prop_simple_name(X, "parse").

% Edge shortcut: call edges.
? code_calls(A, B).

% Bounded traversal (with explicit depth and status).
? code_search("Dispatcher", 10, S).
? code_callers(S, Caller, 8, Status).
? code_callees(S, Callee, 8, Status).
? code_path(From, To, Path, Rank, Status).
? code_cycle(Symbol, Cycle, Status).

JSON/HTTP

The same CodeQuery AST is used by both Prolog and JSON. execute_json parses a JSON request and returns a uniform result shape:

{
  "op": "search",
  "query": "parse",
  "top_k": 10
}
{
  "op": "search",
  "answers": [
    { "symbol_id": "code:sym:...", "score": 3.21 }
  ],
  "exhausted": []
}

CodeQuery AST

Variant Input Output
search query string, top_k, filters ranked symbol hits
callers symbol id, budgets reachable callers (deduplicated)
callees symbol id, budgets reachable callees (deduplicated)
path from, to, top_n, budgets top-N shortest simple paths
cycle symbol id, budgets simple cycles containing the symbol
expand symbol ids, edge kinds, budgets adjacent symbols

All requests carry CodeBudgets (depth, visited, edges, paths, timeout) and an exhausted vector reporting which budget was hit.

CLI

# Initialize (full build from a scan root).
zlf code init --scan-root ./my-repo --db ./zlf-db --repo my-repo

# Full rebuild (re-import every file).
zlf code index --scan-root ./my-repo --db ./zlf-db --repo my-repo

# Incremental sync (only changed/deleted files).
zlf code sync --scan-root ./my-repo --db ./zlf-db --repo my-repo

# Query.
zlf code search "parse" --top-k 10 --kind function --lang python
zlf code callers <symbol-id> --depth 8
zlf code callees <symbol-id> --depth 8
zlf code path <from> <to> --top-n 8 --depth 32
zlf code cycle <symbol-id> --depth 8
zlf code expand sym1,sym2 --kinds calls,imports --depth 8

All query subcommands print a uniform JSON result with op, answers, and exhausted fields.

HTTP API

The same operations are available through the HTTP server (zlf serve):

Endpoint Operation
POST /api {"command":"code_init", ...} full build
POST /api {"command":"code_index", ...} full rebuild
POST /api {"command":"code_sync", ...} incremental sync
POST /api {"command":"code_query", "query":{...}} any CodeQuery

The code_query endpoint accepts the same CodeQuery JSON shape used by Prolog and the Rust API. All endpoints accept an optional path field (defaults to the server’s configured db_path).

{"command": "code_query", "query": {"op": "search", "query": "parse", "top_k": 10}}
{"command": "code_query", "query": {"op": "callers", "symbol": "code:sym:...", "budgets": {"depth": 8}}}

Analyzer

The code_symbol analyzer stores the normalized complete identifier plus identifier-boundary subtokens: ServiceDispatcherservice + dispatcher. Matching is boundary-subtoken only (no ngram, suffix, or fuzzy).

Benchmark

Run the smoke tier (1 000 files):

ZLF_BENCH_TIER=smoke cargo test -p zlf-codeindex --test bench_runner --ignored --nocapture

Tiers: smoke (1 000 files) / mid (10 000) / target (100 000). The runner prints a machine-readable JSON report and asserts: