meta-ast Documentation
This directory contains the implementation-facing technical documentation for
meta-ast.
The structure is intentionally traceable: specs -> architecture -> structure -> ADRs -> roadmap -> validation artifacts, so design decisions are easy to trace back to requirements and forward to tests.
Document map
ARCHITECTURE.md- system architecture, component boundaries, runtime flow, output formats, and themetacall-deployfeature layer.STRUCTURE.md- code structure, data structures, design patterns, module layout, and implementation order.DEPLOY.md- MetaCall deploy manifests: scanner, pod partitioning, mesh annotation.DEV_CRATE_DECISIONS.md- crate selection rationale and trade-offs.CI_CD.md- CI/CD architecture and quality gates.ROADMAP.md- phase-aligned implementation milestones and measurable exit gates.BENCHMARKS.md- criterion benchmark results for pipeline, graph, and incremental.DEMO.md- recorded CLI walkthroughs (GIFs) aligned to the delivered artifacts.FINAL_REPORT.md- GSoC 2026 completion report.
Specs
specs/requirements.md- normative requirements and acceptance criteria.specs/graph-model.md- symbol graph and datagraph contracts, includinglanguage_id, project-root-relativepath,snapshot_id,file_id,visibility, andDataNodesemantics.specs/symbol-extraction.md- language-pack extraction contracts.specs/traceability.md- mapping from deliverables to implementation/docs/tests.
Architecture Decision Records (ADRs)
adr/0001-stateful-import-resolver.md- Stateful import resolver trait seamadr/0002-scope-resolution-heuristics.md- Scope resolution heuristicsadr/0003-unresolved-import-policy.md- Unresolved import handling policyadr/0004-global-scope-synthetic-symbols.md- Global scope synthetic symbol generation
Requests for Comments (RFCs)
rfcs/0001-language-loading-model.md- Language loading modelrfcs/0002-error-semantics-and-recovery.md- Error semantics and recoveryrfcs/0003-incremental-parsing-strategy.md- Incremental parsing strategyrfcs/0004-graph-representation-and-scc.md- Graph representation and SCCrfcs/0005-output-contract-policy.md- Output contract policyrfcs/0006-type-inference-scope.md- Type inference scoperfcs/0007-dgraph-integration-scope.md- Dgraph integration scoperfcs/0008-graph-module.md- Graph module designrfcs/0009-cross-file-dependency-mapping.md- Cross-file dependency mappingrfcs/0010-deploy-manifests.md- MetaCall deploy manifests (superseded by the pod model)rfcs/0011-metacall-client-api-support.md- MetaCall client call support:metacall()and the full invocation surfacerfcs/0012-polyglot-lsp-server.md- Polyglot LSP Server proposal: architecture, protocol, and deployment
Scope policy
- MVP (must ship): symbol extraction, dependency graph + SCC/Deployment Unit analysis, cross-platform CI.
metacall-deployfeature: Cross-Language Call Site detection, Deploy Manifest generation, Root Manifest assembly, Mesh Annotation from SCC.- Stretch: intra-procedural dataflow beyond simple def-use, live Dgraph sink, advanced cross-language type matching, expanded language support.
Update policy
When implementation changes any public contract (schema, CLI behavior, graph semantics, language support), update the corresponding file in this directory in the same pull request.
Architecture
1. Design goals
- Standalone-first static analysis (no runtime execution).
- Deterministic and resilient extraction under partial syntax errors.
- Incremental-by-design workflow for watch/update scenarios.
- Language-agnostic core; MetaCall deployment support is an opt-in feature.
2. High-level pipeline
- Source discovery and language detection.
- Tree-sitter parse per file.
- Query-based symbol extraction per language pack.
- Intermediate symbol model normalization.
- Dependency graph construction (initial node + file edges).
- Import path resolution via stateful resolvers implementing the
ImportResolvertrait (mapping import strings to file paths/IDs, supporting stateful configs liketsconfig.jsonand disk caches). - Cross-file reference resolution via
FlattenedScopeCache(DFS the import graph once per file, then O(1) scope lookups). - SCC analysis (Tarjan) and Deployment Unit annotation.
- Output emission (JSON, YAML, or interactive HTML dashboard).
- (Requires
metacall-deployfeature) Cross-language call-site scanning, pod partitioning, dependency resolution from lockfiles, pod-and-mesh manifest generation, and CI fairness checking. See DEPLOY.md.
3. Component boundaries
- Input layer: path discovery, filtering, language routing.
- Parser layer: Tree-sitter parser lifecycle and tree ownership.
- Extractor layer: language-specific query packs and capture mapping.
- Model layer: normalized symbol/domain structs.
- Graph layer: directed graph assembly + SCC algorithms. External dependencies (stdlib, third-party packages) that are referenced but not part of the project are represented as
ExternalNodeentries (graph/node.rs:85), carrying the raw import path and language. They appear in the graph but have no file-backed symbol data. - Pipeline layer: full graph analysis orchestration (
pipeline.rs). - Resolver layer: cross-file reference resolution via
FlattenedScopeCache(graph/resolver.rs). - Output layer: serialization and optional adapters.
- Interface layer: CLI + library API (future: C ABI).
- Deploy layer (feature-gated:
metacall-deploy): Cross-language call-site scanner (scanner.rs), pod partitioning via Union-Find over same-language edges (pod.rs), cross-language SCC cut detection and oversized-pod rebalancing (cut.rs), per-language external dependency resolution from lockfiles and manifests (dependency.rs), pod manifest generation (manifest.rs), Function Mesh annotation (mesh.rs), and CI fairness checking for RPC-converted cut edges (check.rs). See DEPLOY.md.
Detailed module layout, data structures, and dependency direction are defined in STRUCTURE.md.
4. Data contracts (summary)
Primary symbol extraction output:
funcsclassesobjects
Static extensions:
source_rangedocstring(where available)
Deploy output (feature-gated: metacall-deploy):
metacall.pods.json- pod manifest with per-pod deployments, inter-pod edges, dependency lists, and AST node metricsmetacall.mesh.json- Function Mesh topology annotation with SCC-derived deployment units and cross-language call-site attribution
See DEPLOY.md for schema details and the call site scanner reference.
Detailed graph contract is defined in specs/graph-model.md.
5. Error handling model
- Parse errors are recoverable when Tree-sitter yields partial trees.
- Extraction errors are scoped to file/language unit where possible.
- Unresolvable Cross-Language Call Sites (dynamic tag/path arguments) are annotated as low-confidence entries in the Mesh Annotation, not silently dropped.
- Fatal process-level errors are reserved for invalid configuration or unrecoverable IO/system failures.
6. Incremental analysis model
- Baseline mode: re-parse changed file and recompute the full graph.
- Optimized mode: apply
InputEdit+ changed range reduction (planned, benchmark-triggered).
Current status: Baseline incremental analysis is implemented behind the watch
feature flag (--features watch). The watch module (src/watch/mod.rs) provides:
incremental_reanalyze()- pure, deterministic, testable re-analysis step.run_watch()- debounced OS-level watcher loop (vianotify+notify-debouncer-mini).- BLAKE3 cryptographic content-hash fingerprinting (
Fingerprint([u8; 32])) for change detection. - Cached
Arc<FileExtraction>per file: zero-allocation pointer sharing for unchanged files; graph rebuilt from scratch each tick (graph + SCC rebuild is sub-ms). - CLI integration via
meta-ast graph <path> --watch [--watch-debounce <ms>].
The InputEdit / changed-range narrowing optimization is deferred per RFC 0003.
Parallel parse + extract uses rayon per-file; graph assembly is sequential. See STRUCTURE.md section 5 for pipeline phase details.
7. Output formats
The CLI supports JSON and YAML for programmatic consumption, plus an interactive HTML dashboard for visual analysis and datagraph JSON exports.
- JSON / YAML: Controlled by the
-f, --formatflag. JSON is the default. YAML requires no extra setup - just pass--format yaml. - HTML dashboard: Separate concern, activated with
--html. Generates a single.htmlfile with an interactive Cytoscape.js graph loaded from a CDN (cached by the browser after first fetch). The browser auto-opens unless you redirect. - Datagraph JSON: Activated with
--datagraph(requires--features dataflow). Exports detailed data/flow node definitions and def-use relations. - Language Filter: Only analyze files detected as this language with
-l, --language <lang>.
The dashboard turns SCC analysis into something you can actually see. Nodes in cyclic clusters (co-deployment required) are colored red. Independent Deployment Units are green. This is the difference between “your code has cycles” and “here is the exact knot you need to untangle before you can split this into independent mesh units.”
8. Compatibility and integration
- Optional integration layers (C ABI,
metacall-deploy, Dgraph) are feature-scoped and do not block standalone operation. Themetacall-deployfeature is implemented (see DEPLOY.md). C ABI is planned but not yet implemented. - Discussion and contributions: Discord
Code Structure and Design Plan
This document defines the module layout, data structures, design patterns, language features, testing strategy, and implementation order for meta-ast. It is the authoritative reference for how code is organized and why.
1. Module Structure
src/
├── lib.rs Public API re-exports
├── main.rs CLI entrypoint
├── error.rs Error + Diagnostic types (thiserror)
├── pipeline.rs Full graph analysis orchestration
│
├── model/
│ ├── mod.rs Symbol, SymbolKind, SourceRange, UnresolvedImport, UnresolvedReference, FileExtraction, DataNode, DataScope, FlowEdge, FlowKind (feature: dataflow)
│ ├── ids.rs FileId, SymbolId, SnapshotId, DataNodeId (newtyped NonZeroU32 via define_id_type! macro; generator starts at 1)
│ └── output.rs InspectOutput, FuncEntry, ClassEntry, ObjectEntry
│
├── language/
│ ├── mod.rs LangId enum, LanguageSpec struct, DefaultVisibility, DocCommentConfig
│ ├── common.rs extract_with_spec, extract_imports_and_references_with_spec, associate_docstrings
│ ├── dataflow.rs extract_dataflow() dispatcher (feature: dataflow; Rust impl in rust.rs)
│ ├── python.rs Python queries + extraction
│ ├── javascript.rs JavaScript queries + extraction
│ ├── typescript.rs TypeScript queries + extraction
│ ├── tsx.rs TSX queries + extraction (separate grammar from TS)
│ ├── c.rs C queries + extraction
│ ├── cpp.rs C++ queries + extraction
│ ├── rust.rs Rust queries + extraction
│ ├── go.rs Go queries + extraction
│ ├── ruby.rs Ruby queries + extraction
│ └── import_resolver.rs ImportResolver trait, stateful resolvers (Python, Go, JS, TS)
│
├── input/
│ └── mod.rs File discovery, filtering, language routing
│
├── parser/
│ └── mod.rs Tree-sitter parser lifecycle, parse function
│
├── extractor/
│ └── mod.rs Pipeline orchestration: parallel parse + extract per-file (symbols + imports + references)
│
├── graph/
│ ├── mod.rs CodeGraph (DiGraph), add_edge_normalized_with_flow, re-exports
│ ├── node.rs NodeData enum (File / Symbol / External / Data)
│ ├── edge.rs EdgeKind enum (Ownership / Import / Reference / Flow) with confidence + flow_kind
│ ├── builder.rs GraphBuilder, from_extractions, add_data_node, add_flow_edge, import_adjacency
│ ├── scc.rs Tarjan SCC + DeployabilityHint
│ └── resolver.rs FlattenedScopeCache, ResolutionContext, resolve_all_references
│
├── output/
│ ├── mod.rs OutputFormat enum (Json / Yaml) with serialize dispatch
│ ├── emitter.rs EmitConfig, emit_inspect(), emit_graph() - CLI output dispatch
│ ├── inspect.rs Inspect-compatible JSON/YAML emission
│ ├── graph.rs Unified GraphOutput (schema_version, metadata, nodes, edges, sccs, deployability)
│ ├── shard/ `.metast` v2 stable-name JSONL shard and index persistence
│ │ ├── mod.rs Module root, re-exports, unit tests
│ │ ├── error.rs ShardError enum
│ │ ├── file.rs ShardFile, ShardSymbol, write_shard(), read_shard()
│ │ ├── edge.rs ShardEdge, ShardEdgeKind, restore_shard_edges()
│ │ ├── manifest.rs ShardManifestRecord, write_manifest(), read_manifest()
│ │ ├── header.rs ShardHeader, write_header(), read_header()
│ │ └── name.rs Stable naming, descriptors, and parent hierarchy resolution
│ └── dashboard.rs Interactive HTML dashboard (Cytoscape.js via CDN, --html)
│
└── sink/ [feature: dataflow] GraphSink trait + JsonSink
└── mod.rs GraphSink trait, JsonSink (file/stdout)
│
└── interface/ CLI layer
├── mod.rs CLI module root
└── args.rs Clap derive structs (Inspect, Graph, Deploy + -l, --format, --html, --datagraph, --watch, --watch-debounce, -o, --check)
│
├── watch/ [feature: watch]
│ └── mod.rs IncrementalCache, WatchState, incremental_reanalyze, run_watch
│
└── deploy/ [feature: metacall-deploy] See [DEPLOY.md](DEPLOY.md)
├── mod.rs Entry: run_deploy(), DeployConfig, add_metacall_edge()
├── scanner.rs tree-sitter call-site detection, CallSite, CallSiteVariant, confidence
├── pod.rs Union-Find partition_into_pods(), PodPartition, InterPodEdge
├── cut.rs find_cross_language_cuts(), find_oversized_pod_cut(), CutEdge
├── dependency.rs classify_external(), resolve_dependencies(), per-language resolvers
├── metrics.rs compute_file_metrics(), compute_pod_metrics(), FileMetrics
├── manifest.rs generate_pod_manifest(), PodManifest, ManifestEdge
├── mesh.rs generate_mesh_annotation(), DeploymentUnit, CrossLanguageEdge
├── check.rs check_cut_fairness() - bijection check between cuts and rpc_stub edges
└── tags.rs LangId <-> MetaCall runtime tag mapping
Module dependency direction
CLI (interface/)
→ Pipeline (pipeline.rs) → orchestrates the full graph analysis
→ Extractor (extractor/) → depends on model + language + parser
→ Parser (parser/) → depends on language (grammar dispatch)
→ Graph (graph/) → depends on model + petgraph
→ Resolver (graph/resolver.rs) → cross-file reference resolution
→ Input (input/) → depends on language (detection)
→ Output (output/) → depends on model + graph
→ Deploy (deploy/) → depends on pipeline + graph + input [feature: metacall-deploy]
→ Sink (sink/) → depends on output/graph [feature: dataflow]
→ Error (error.rs) ← cross-cutting
Outer layers depend on inner layers. The model layer has zero knowledge of parsing, I/O, or language specifics.
2. Core Data Structures
2.1 ID Types
Newtyped NonZeroU32 values generated by define_id_type! and allocated by
IdGenerator<T> (an AtomicU32 wrapper) for lock-free, thread-safe,
session-deterministic allocation. Type-safe against mixing.
The generator starts at 1: 0 is the permanently invalid niche value, so
Option<Id> niche-optimizes to 4 bytes (the size of Id itself) instead of
the 8 bytes an Option<u32> would cost. This benefits structures that store an
optional id, e.g. DataNode.symbol_id: Option<SymbolId>.
#![allow(unused)]
fn main() {
define_id_type!(FileId);
define_id_type!(SymbolId);
define_id_type!(SnapshotId);
define_id_type!(DataNodeId);
}
Construction is fallible: Id::new(u32) -> Option<Self> returns None for 0
(rejecting the niche value at the type boundary, including deserialization).
The raw value is reachable via Id::to_raw() -> u32 and From<NonZeroU32>.
2.2 Source Location
#![allow(unused)]
fn main() {
pub struct LineColumn {
pub line: usize, // 0-indexed
pub column: usize, // 0-indexed, byte offset within line
}
pub struct SourceRange {
pub byte_start: usize,
pub byte_end: usize,
pub start: LineColumn,
pub end: LineColumn,
}
}
2.3 Symbol Model
Immutable IR - constructed once during extraction, never mutated.
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub enum SymbolKind {
Function,
Method,
Class,
Struct,
Interface,
Trait,
Enum,
Object,
Constant,
Static,
Module,
Namespace,
TypeAlias,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[non_exhaustive]
pub enum Visibility {
Public,
Private,
}
#[derive(Debug, Clone, Serialize)]
pub struct Symbol {
pub id: SymbolId,
pub name: String,
pub kind: SymbolKind,
pub language: LangId,
pub file_path: PathBuf,
pub source_range: SourceRange,
pub visibility: Option<Visibility>,
pub signature: Option<String>,
pub docstring: Option<String>,
pub is_async: bool,
}
}
2.4 Graph Model
Node and edge types:
| Node | Fields |
|---|---|
FileNode | id, path (project-root-relative), language, snapshot_id |
SymbolNode | id, name, kind, file_id, visibility, source_range |
ExternalNode | raw_path, language |
| Edge | Direction |
|---|---|
Ownership | FileNode -> SymbolNode, SymbolNode -> SymbolNode (nesting) |
Import | FileNode -> FileNode |
Reference | SymbolNode -> SymbolNode |
Graph invariants:
- Every SymbolNode maps to exactly one FileNode.
- Ownership edges form an acyclic containment structure.
- SCC applies to dependency/reference subgraph only (Ownership excluded).
- Duplicate edges normalized by
(src, dst, edge_kind). - External dependencies get
NodeData::Externalplaceholder nodes.
2.5 Inspect Output
Stable contract:
#![allow(unused)]
fn main() {
pub struct InspectOutput {
pub funcs: Vec<FuncEntry>,
pub classes: Vec<ClassEntry>,
pub objects: Vec<ObjectEntry>,
}
}
Each entry type includes: name, source_range, optional signature, visibility, docstring. FuncEntry additionally includes an async flag.
3. Language System Design
3.1 LanguageSpec Struct
Each language is a static LanguageSpec constant with function pointers (not a trait):
#![allow(unused)]
fn main() {
pub struct LanguageSpec {
pub extensions: &'static [&'static str],
pub grammar_fn: fn() -> tree_sitter::Language,
pub query_fn: fn() -> &'static Query,
pub import_path_resolver: fn(&str, &Path, &Path) -> Option<PathBuf>,
pub import_ref_query_fn: fn() -> &'static Query,
pub class_like_parents: &'static [&'static str],
pub ancestor_visibility_rules: &'static [(&'static str, Visibility)],
pub visibility_from_name: Option<fn(&str) -> Option<Visibility>>,
pub import_statement_kinds: &'static [&'static str],
pub default_visibility: DefaultVisibility,
pub doc_comment_config: Option<DocCommentConfig>,
}
}
3.2 LangId Enum
The aggregate dispatch enum. #[non_exhaustive] for forward compatibility:
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, strum::Display, strum::AsRefStr)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[repr(usize)]
pub enum LangId {
Python,
JavaScript,
TypeScript,
Tsx,
C,
Cpp,
Rust,
Go,
Ruby,
}
}
3.3 Stateful Import Resolution Seam
To support complex stateful import resolution (e.g. resolving paths using configuration files like tsconfig.json or module boundary scanning like go.mod), meta-ast implements a hybrid seam combining static LanguageSpec specs with a stateful ImportResolver trait:
#![allow(unused)]
fn main() {
pub trait ImportResolver: Send + Sync {
fn resolve(
&self,
raw: &str,
source_dir: &Path,
project_root: &Path,
) -> Option<PathBuf>;
}
}
Hybrid Resolution Bridge
LanguageSpecremains static andconst(containing a statelessimport_path_resolverfn pointer).ImportResolverrepresents a stateful trait interface.- Concrete adapters bridge the two:
StatelessResolver: Zero-cost wrapper delegating to static fn pointers.PythonResolver,GoModResolver,JsResolver,TsConfigResolver: Concrete structs implementingImportResolver, prepped to hold caches or parse configs.
make_resolver(LangId) -> Box<dyn ImportResolver>: Factory function constructing the stateful resolver for each language dynamically.
Stateful Caching and Memoization Engines
To guarantee maximum throughput and avoid redundant filesystem traversal during large-scale workspace parsing, the stateful resolvers employ optimized, thread-safe caching strategies:
OnceLockModule Boundary Scanning (GoModResolver): Scans for the rootgo.modfile and parses the module path at most once per execution using a standardOnceLock. Subsequent resolution calls query the in-memory boundary in $O(1)$ time.RwLockFile Existence Memoization (PythonResolver,JsResolver,TsConfigResolver): Memoizesexists()andis_file()filesystem checks using anRwLock<HashMap<PathBuf, bool>>. This minimizes expensive system calls during TypeScript candidate extensions resolution (e.g. trying.ts,.tsx,.js) and Python relative path matching, while remaining safe for concurrency.- Stateless Fallback: When candidate paths do not match or cannot be resolved using stateful logic, all resolvers gracefully fallback to their underlying stateless
LanguageSpecfunction pointer, ensuring 100% backward compatibility.
During graph assembly, resolvers are created once per run and cached inside the builder to ensure O(1) config-file reading and caching properties.
3.4 Adding a New Language
The process is:
- Add the tree-sitter grammar crate to
Cargo.toml. - Create
src/language/<name>.rswith query constants, extraction function, andLanguageSpecconstant. - Add a variant to
LangIdenum. - Add a match arm in
spec_for(). - Add fixture files and tests.
No trait objects, no runtime plugins. Compile-time completeness checking via exhaustive match.
3.5 Language Detection
detect_language(path: &Path) -> Option<LangId> maps file extensions to LangId variants. Lives in input/mod.rs.
| Extension(s) | LangId |
|---|---|
.py, .pyi | Python |
.js, .mjs, .cjs | JavaScript |
.ts, .cts, .mts | TypeScript |
.tsx | Tsx |
.c | C |
.cc, .cpp, .cxx | Cpp |
.rs | Rust |
.go | Go |
.rb, .gemspec | Ruby |
4. Design Patterns
4.1 Enum Static Dispatch (Language System)
All language-specific behavior dispatches through match on LangId. No vtables, no dyn - full monomorphization and inline optimization.
4.2 Pipeline Pattern
The analysis pipeline is orchestrated by pipeline.rs:
Source Discovery -> Parallel Parse + Extract -> Graph Assembly -> Import Resolution -> Reference Resolution -> SCC -> Output
(sequential) (rayon par_iter) (sequential) (sequential) (sequential) (sequential)
Parse and extract are combined per-file to avoid materializing all tree-sitter trees simultaneously.
4.3 Newtype Pattern
FileId, SymbolId, SnapshotId are newtyped u32 values via define_id_type! macro. The compiler prevents mixing them, and #[serde(transparent)] keeps serialization clean.
4.4 Recoverable Error Accumulation
Parse errors do not abort extraction. The pipeline accumulates Vec<Diagnostic> alongside results. Tree-sitter ERROR and MISSING nodes are skipped during extraction. Diagnostics are a separate concern from the symbol model.
4.5 Immutable IR
Symbol structs are constructed during extraction and never mutated. Downstream consumers (graph assembly, output serialization) read them immutably.
5. Parallelism Strategy
5.1 rayon Integration
rayon = "1.10" is used for file-level parallelism in the parse + extract phase.
- A thread-local pool of
Parserinstances (one per language) is maintained within each worker thread viathread_local!andRefCellcaching. This avoids sharing the non-SyncParseracross threads. - Emitted
Treeand symbol models areSendand are safely returned from rayon workers to the main thread for graph assembly. - Caching
Parserinstances avoids redundant grammar re-initialization and allocation overhead on every task.
5.2 Pipeline Phases
| Phase | Concurrency | Rationale |
|---|---|---|
| File discovery | Sequential | Single walk, fast I/O |
| Parse + Extract | rayon par_iter | CPU-bound, per-file independent, largest time slice |
| Graph assembly | Sequential | petgraph mutation + cross-file resolution requires single-threaded access |
| Import resolution | Sequential | Uses per-language import path resolvers |
| Reference resolution | Sequential | FlattenedScopeCache + cross-file lookup |
| Output serialization | Sequential | Single JSON/YAML document emission |
6. Error Handling
6.1 Error Type Hierarchy
#![allow(unused)]
fn main() {
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("IO: {0}")]
Io(#[from] std::io::Error),
#[error("parse error in {path}: {message}")]
Parse { path: PathBuf, message: String },
#[error("query error ({language}): {message}")]
Query { language: LangId, message: String },
#[error("config: {0}")]
Config(String),
#[error("graph error: {0}")]
Graph(String),
}
}
Library uses Result<T, Error> with ? propagation. Application boundary (CLI) uses anyhow::Result.
6.2 Diagnostics
#![allow(unused)]
fn main() {
pub struct Diagnostic {
pub path: PathBuf,
pub severity: Severity, // Warning, Error
pub message: String,
pub source_range: Option<SourceRange>,
}
}
Diagnostics are accumulated in a Vec<Diagnostic> separate from the symbol model. Extraction continues on recoverable errors.
6.3 Error Recovery Rules
- Tree-sitter
ERRORandMISSINGnodes are skipped during extraction. - Partial extraction is allowed and expected for malformed source files.
- If > 50% of a file’s nodes are errors, the file is marked as unparseable but does not abort the pipeline.
- Fatal errors are reserved for invalid configuration or unrecoverable I/O failures.
6.4 Query Compilation Failure Strategy
Tree-sitter queries are hardcoded constants in each language pack. If a query fails to compile, it indicates a programmer bug in the shipped query text, not a runtime input error.
Strategy: compile_query uses panic!() rather than std::process::abort() or Result propagation.
Why not abort(): panic!() runs destructors, is propagated by rayon, and integrates with Rust’s panic infrastructure. abort() skips all cleanup.
Why not Result: Queries are compiled inside LazyLock<T>::new() closures which require FnOnce() -> T (infallible return).
Mitigation: language::validate_queries() eagerly initializes all 16 LazyLock statics at startup, ensuring any query bug panics immediately rather than after processing files.
7. Rust Language Features Used
| Feature | Usage |
|---|---|
| Edition 2024 | MSRV 1.94.0 |
#[non_exhaustive] | All public enums (LangId, SymbolKind, Visibility, Severity) |
| Newtype pattern | FileId, SymbolId, SnapshotId, DataNodeId via define_id_type! macro (NonZeroU32 inner, 1-based generator) |
impl From<X> for Error | Automatic error conversion for ? propagation |
AtomicU32 | Thread-safe ID generation (counter starts at 1; 0 is the invalid NonZeroU32 niche) |
NonZeroU32 niche | Option<Id> collapses to 4 bytes via the NonZeroU32 niche optimization |
serde derive | All serializable types with #[serde(rename_all = "snake_case")] |
thiserror derive | Error types with formatted messages |
clap derive | CLI argument structs |
rayon par_iter | File-level parallelism |
strum derives | LangId display/serialization |
LazyLock | Language query static initialization |
8. Dependencies
8.1 Runtime Dependencies
| Crate | Version | Purpose |
|---|---|---|
tree-sitter | 0.26.11 | Core parsing |
tree-sitter-python | 0.25.0 | Python grammar |
tree-sitter-javascript | 0.25.0 | JavaScript grammar |
tree-sitter-typescript | 0.23.2 | TypeScript + TSX grammars |
tree-sitter-c | 0.24.2 | C grammar |
tree-sitter-cpp | 0.23.4 | C++ grammar |
tree-sitter-rust | 0.24.2 | Rust grammar |
tree-sitter-go | 0.25.0 | Go grammar |
tree-sitter-ruby | 0.23.1 | Ruby grammar |
petgraph | 0.8.3 | Directed graph + Tarjan SCC |
serde + serde_json | 1.0 | JSON serialization |
yaml_serde | 0.10 | YAML serialization |
strum | 0.28 | Enum derive macros (Display, AsRefStr) |
webbrowser | 1.2 | Auto-open HTML dashboard in browser |
clap | 4.6 | CLI (derive API, env, color) |
rayon | 1.12 | Parallel file processing |
thiserror | 2.0 | Library error types |
anyhow | 1.0 | Application error boundary |
dunce | 1.0 | Cross-platform path canonicalization |
ignore | 0.4 | Gitignore-aware file walking |
blake3 | 1.5 | Cryptographic content hashing (optional under watch) |
notify | 8.2 | File system notification watcher (optional under watch) |
notify-debouncer-mini | 0.7 | Debounced event loop (optional under watch) |
tracing + tracing-subscriber | 0.1 / 0.3 | Structured diagnostics |
8.2 Development Dependencies
| Crate | Version | Purpose |
|---|---|---|
insta | 1.48 | Snapshot testing for JSON output contracts |
criterion | 0.8 | Benchmark gating (pipeline, graph, incremental) |
tempfile | 3.27 | Temporary filesystem test fixtures |
8.3 Feature Flags
| Feature | Purpose |
|---|---|
metacall-deploy | Generate MetaCall deployment manifests and mesh annotations |
dataflow | Data/flow node tracking and def-use graph extraction |
watch | Debounced file-system watch mode with incremental re-analysis |
9. Test Structure
tests/
├── integration.rs Integration test module root
├── integration/
│ ├── pipeline_test.rs End-to-end: discover -> parse -> extract -> graph -> output
│ ├── dashboard_test.rs HTML dashboard generation tests
│ ├── output_format_test.rs JSON/YAML output format tests
│ └── inspect_output_test.rs Inspect-compatible output validation
└── fixtures/
├── python/
│ ├── simple_functions.py
│ ├── classes.py
│ ├── async_decorators.py
│ ├── deep_nesting.py
│ ├── partial_syntax_error.py
│ └── sample.py
├── javascript/
│ ├── functions.js
│ ├── classes.js
│ └── large_classes.js
├── typescript/
│ └── interfaces.ts
├── tsx/
│ └── components.tsx
├── c/
│ ├── functions.c
│ └── structs_enums.c
├── cpp/
│ ├── classes.cpp
│ └── namespaces.cpp
├── rust/
│ ├── functions.rs
│ ├── structs_enums.rs
│ └── large_file.rs
├── go/
│ ├── functions.go
│ ├── methods.go
│ └── deep_nesting.go
├── mixed/ Multi-language single-directory fixtures
│ ├── app.py, index.js, main.rs, test.generated.py
│ └── auth_microservice{,_level2,_level3} Deploy edge-case fixtures
│ (star / cross-language SCC cycle / full-module stress)
└── multi/ Multi-file cross-language fixtures
├── main.py, lib.py, app.js, util.js
├── c_app/, cpp_app/, go_app/, rust_crate/, ts_app/, tsx_app/
└── edge_*/ Edge case fixtures (circular, alias, shadowing, etc.)
Snapshot policy
Insta snapshot files live in src/language/snapshots/ as .snap files (not under fixture directories). Each
language module generates snapshots via inline unit tests. Update workflow: cargo insta test then
cargo insta review then commit accepted .snap files.
Testing Strategy
| Layer | Tool | Purpose |
|---|---|---|
| Language detection | Unit tests | Extension-to-LangId mapping |
| Per-language extraction | Fixture files + unit tests | Query correctness, capture mapping |
| JSON output contract | insta snapshots in src/language/snapshots/ | Regression detection |
| Error recovery | Fixture with invalid syntax | Partial results, no panics |
| End-to-end pipeline | Integration tests | Full discover -> output flow |
| Deploy module | Tiered mixed/auth_microservice* fixtures + cut.rs unit tests | Cross-language SCC cut, intra-language collapse, oversized-pod, load variants, dependency classification |
| Performance | criterion benchmarks | Extraction throughput |
Deploy Module (metacall-deploy)
Feature-gated. Build with --features metacall-deploy.
Overview
The deploy subcommand scans polyglot projects for MetaCall load and client call sites. It partitions files into same-language pods, resolves external dependencies from lockfiles, and generates two deployment artifacts:
| Artifact | Description |
|---|---|
metacall.pods.json | Pod manifest with deployment units, inter-pod edges, dependency lists, and AST metrics |
metacall.mesh.json | Function Mesh topology with SCC deployment units and call-site attribution |
Usage
# Build with the feature enabled
cargo build --release --features metacall-deploy
# Generate manifests
meta-ast deploy <path> --out <output_dir>
# CI validation: verify every cut edge has an RPC stub entry
meta-ast deploy <path> --check
Options
| Flag | Default | Description |
|---|---|---|
-o, --out <dir> | . | Directory to write generated artifacts |
-f, --format <json|yaml> | json | Serialization format |
--check | off | Fairness check mode: exits non-zero on missing RPC stubs |
--max-pod-size <N> | 20 | Files per pod before rebalancing triggers |
Pipeline
run_deploy()
1. discover_files() - language-routed file list
2. pipeline::analyze_graph() - symbol, import, and SCC analysis
3. scanner::scan_file() per file (rayon parallel) - MetaCall call-site detection
4. inject MetaCall import edges into graph - add_metacall_edge with path resolution
5. resolve client calls (two-phase) - client_call::resolve_client_calls
6. SCC recompute with new edges - update SCC analysis
7. pod::partition_into_pods() - Union-Find over same-language edges
8. metrics::compute_file_metrics() - AST node counts per file/pod
9. cut::find_cross_language_cuts() - cheapest-edge split for cross-lang SCCs
10. cut::find_oversized_pod_cut() per pod - second-pass rebalancing
11. dependency::resolve_dependencies() - lockfile and manifest parsing
12. manifest::generate_pod_manifest() - PodManifest serialization
13. mesh::generate_mesh_annotation() - SCC-derived topology
14. write artifacts or check::check_cut_fairness() in --check mode
Module map
src/deploy/
├── mod.rs Entry point: run_deploy(), DeployConfig, add_metacall_edge()
├── scanner.rs tree-sitter call-site detection, CallSite, CallSiteVariant
├── client_call.rs resolve_client_calls(), resolve_script_to_file() - two-phase client invocation resolution
├── pod.rs Union-Find partition_into_pods(), PodPartition, InterPodEdge
├── cut.rs find_cross_language_cuts(), find_oversized_pod_cut(), CutEdge
├── dependency.rs classify_external(), resolve_dependencies(), per-language resolvers
├── metrics.rs compute_file_metrics(), compute_pod_metrics(), FileMetrics
├── manifest.rs generate_pod_manifest(), PodManifest, ManifestEdge
├── mesh.rs generate_mesh_annotation(), DeploymentUnit, CrossLanguageEdge
├── check.rs check_cut_fairness() - bijection check between cuts and rpc_stub edges
└── tags.rs LangId <-> MetaCall tag mapping (py, node, ts, c, cpp, rs, go)
Call Site Scanner
scanner::scan_file runs tree-sitter queries to detect MetaCall API load variants and client calls.
Supported variants
| Variant | Detected functions |
|---|---|
LoadFromFile | metacall_load_from_file, LoadFromFile (Go), bare use import (Rust), load::from_single_file (Rust) |
LoadFromMemory | metacall_load_from_memory, LoadFromMemory |
LoadFromPackage | metacall_load_from_package, LoadFromPackage |
LoadFromConfiguration | metacall_load_from_configuration, LoadFromConfiguration |
ClientCall | metacall, metacall_await, metacallfms (all); metacallv, metacallt, metacall_function (C/C++); Go metacall.Call / metacall.Await; Rust metacall::metacall, metacall_no_arg, metacall_untyped. Note: metacall_handle is excluded because argument layout varies per port |
Confidence scoring
| Case | Score |
|---|---|
| String literal argument | 1.0 |
| Unique match in load-confirmed files (Phase A) | 1.0 |
| Multiple matches in load-confirmed files (Phase A) | 0.8 |
| Unique match in global name index (Phase B) | 0.6 |
| Multiple matches in global name index (Phase B) | 0.5 |
| Computed argument / function name | 0.4 |
Language coverage
Queries cover all 9 supported languages: Python, JavaScript, TypeScript, TSX, C, C++, Rust, Go, and Ruby.
Client invocation resolution
client_call::resolve_client_calls resolves ClientCall targets after scanning:
- Phase A (Load-aware): Maps caller load sites to candidate target files, matching function names within loaded scope.
- Phase B (Global fallback): Searches a project-wide index of symbol names when Phase A finds no match. Emits a Warning diagnostic if no match exists.
Ambiguity resolution is deterministic: one edge per matching symbol in path-sorted order. Client-call edges are EdgeKind::Reference from calling file to target symbol node.
run_deploy also warns on orphaned metacall.json files that no LoadFromConfiguration call site references.
Pod Partitioning
pod::partition_into_pods uses Union-Find to group files into same-language deployment units.
Files sharing the same LangId and connected by Import or Reference edges join the same pod. Cross-language edges remain inter-pod edges. Ownership edges are excluded because they express file structure, not dependency.
Confidence fusion
When both an Import edge and a Reference edge connect the same pod pair, confidence scores multiply to form a combined weight in [0.0, 1.0]. If only one edge type exists, its confidence is used directly.
Language tag mapping
| Language | Tag |
|---|---|
| Python | py |
| JavaScript | node |
| TypeScript / TSX | ts |
| C | c |
| C++ | cpp |
| Rust | rs |
| Go | go |
Cut Detection
cut.rs implements two cut rules:
- Cross-language SCC cuts: Cuts the lowest-confidence internal edge when an SCC spans multiple languages (
CutReason::CrossLanguageScc). The manifest marks these as RPC stubs. - Oversized pod cuts: Greedy single-pass cut on pods exceeding the pod size limit (
DEFAULT_MAX_POD_SIZE, 20 files by default). Override the limit with--max-pod-size <N>.
External Dependency Resolution
dependency::resolve_dependencies collects external imports per pod and inspects project lockfiles and manifests.
| Language(s) | Resolver | Lockfile (preferred) | Manifest (fallback) |
|---|---|---|---|
| Python | classify_python | uv.lock, poetry.lock, Pipfile.lock | pyproject.toml, requirements.txt |
| JS / TS / TSX | classify_node_ecosystem | package-lock.json, yarn.lock, pnpm-lock.yaml | package.json |
| Rust | classify_rust | Cargo.lock | Cargo.toml |
| Go | classify_go | go.sum | go.mod |
| C / C++ | classify_c_cpp_best_effort | - | conanfile.txt, vcpkg.json |
Lockfiles supply pinned versions (source: "Lockfile"). Manifest fallbacks set version: None (source: "Manifest").
Pod Manifest Schema
manifest::generate_pod_manifest writes metacall.pods.json:
{
"version": "1.0",
"deployments": [
{
"id": 0,
"language": "py",
"files": ["auth.py", "__init__.py"],
"metrics": {
"total_ast_nodes": 63,
"file_count": 2,
"symbol_count": 2
},
"dependencies": [
{
"name": "requests",
"version": "2.32.3",
"language": "python",
"source": "Lockfile"
}
]
}
],
"edges": [
{
"from_pod": 0,
"to_pod": 1,
"kind": "import",
"confidence": 1.0,
"is_cross_language": true,
"cut_annotation": null
}
],
"metrics": {
"total_pods": 2,
"cross_language_edges": 1,
"total_ast_nodes": 112
}
}
Mesh Annotation
mesh::generate_mesh_annotation exports SCC analysis to metacall.mesh.json.
#![allow(unused)]
fn main() {
MeshAnnotation {
version: String,
deployment_units: Vec<DeploymentUnit>,
cross_language_edges: Vec<CrossLanguageEdge>,
stats: MeshStats,
}
DeploymentUnit {
id: usize,
symbols: Vec<UnitSymbol>,
is_cross_language: bool,
is_mesh_candidate: bool,
deployability: String,
}
CrossLanguageEdge {
from_unit: usize,
to_unit: usize,
from_language: String,
to_language: String,
call_site: Option<String>,
confidence: f64,
}
}
Units with is_mesh_candidate = true and is_cross_language = false deploy independently as Function Mesh services.
Check Mode (Fairness)
check::check_cut_fairness validates RPC stub contracts:
- Every cut edge appears in
manifest.edges[]with acut_annotation. - Cut edges have
kind: "rpc_stub". - Non-cut edges omit
cut_annotation.
run_deploy exits non-zero if fairness checks fail.
Edge-case Fixtures
Integration tests use fixtures in tests/fixtures/mixed/:
| Fixture | Coverage |
|---|---|
auth_microservice | Baseline acyclic star graph (py loads go/node/ts). |
auth_microservice_level2 | Cross-language SCC cycle, intra-language cycle collapse, dynamic and config loads. |
auth_microservice_level3 | All four load variants, 3-file cycle, py-go round-trip cut, lockfile classification. |
Extending the Scanner
To add call site detection for a new language:
- Add a
static <LANG>_QUERY: LazyLock<Query>inscanner.rs. - Add a dispatch arm to
scan_file. - Map tags in
tags.rs. - Add unit tests in
scanner.rs.
CI/CD Architecture
1. Objectives
- Enforce correctness and portability.
- Catch regressions early (lint, tests, benchmarks).
- Produce deterministic release artifacts.
2. Workflow layout
ci.yml- single workflow with three jobs: test (nextest + doc tests), build (release artifacts), lint (fmt + clippy + cargo-deny).benchmark.yml- criterion benchmarks and trend tracking (uploads raw reports as artifacts).docs.yml- rustdoc build (deny warnings) and mdbook site publication to GitHub Pages.release.yml- tag-driven release and package publication with changelog generation.
3. Quality gates
Required for protected branch merge (all within ci.yml):
- Lint job green (fmt + clippy + cargo-deny).
- Test matrix green (nextest + doc tests across OS/toolchain).
- Build artifacts generated (release binaries uploaded per OS).
4. Matrix strategy
OS targets:
- Linux (ubuntu-latest)
- macOS (macos-latest)
- Windows (windows-latest)
- Windows ARM64 (windows-11-arm)
Rust channels:
- stable (required)
- nightly (advisory compatibility signal)
5. Caching and artifacts
- Use cargo target cache for CI acceleration.
- Persist benchmark reports as artifacts.
- Publish release binaries per target triple.
6. Branch protection
- Require status checks before merge.
- Require branch up-to-date with target branch.
- Require at least one review approval.
- Require resolved review conversations.
7. Security posture
- Dependency vulnerability scanning on schedule and push.
- Keep lockfile current and reviewed.
- Do not suppress warnings as default policy.
8. Release policy
- Semantic version tags trigger release workflow.
- Generate changelog summary from merged PRs/issues.
- Publish binary artifacts and crate package (when ready).
9. Documentation policy in CI
Any change that touches output contract, graph semantics, or language extraction must update relevant docs under docs/ in the same PR.
10. Toolchain configuration
rust-toolchain.tomlpins channel1.94.0withrustfmtandclippycomponents.deny.tomlenforces license allowlist (MIT, Apache-2.0, BSD, ISC, etc.) and bans wildcard dependencies.clippy.tomlsets MSRV and complexity thresholds.rustfmt.tomlsets edition 2024 formatting rules.
11. Pre-commit hooks (lefthook)
Managed via lefthook.yml. Install with:
https://lefthook.dev/install/
lefthook install
a dev script will be provided later to automate this process
pre-commit (parallel, fast)
cargo fmt --all -- --check- formatting gate.- Trailing whitespace check (
*.rs,*.toml,*.md,*.yml). - Merge conflict marker detection.
- Large file guard (>512KB).
pre-push (sequential, thorough)
cargo clippy --all-targets --all-features -- -D warnings- lint gate.cargo nextest run --all-features- local test gate.
12. Test runner (nextest)
CI and local dev use cargo-nextest for faster test execution.
- Config:
.config/nextest.toml - CI profile:
fail-fast = false(full matrix visibility). - Slow-timeout: 60s per test, terminate after 3 periods.
13. Benchmarks (criterion)
- Dev dependency:
criterion0.8 withhtml_reportsfeature. - Benchmark targets:
benches/pipeline.rs(extraction throughput per language fixture),benches/graph.rs(graph operation throughput). - Profile:
opt-level = 3, LTO enabled (see[profile.bench]inCargo.toml).
14. Release targets
| Target triple | OS | Runner |
|---|---|---|
x86_64-unknown-linux-gnu | Linux (glibc) | ubuntu-latest |
x86_64-unknown-linux-musl | Linux (static) | ubuntu-latest + musl-tools |
aarch64-unknown-linux-gnu | Linux (ARM64) | ubuntu-24.04-arm |
x86_64-apple-darwin | macOS (Intel) | macos-15 |
aarch64-apple-darwin | macOS (Apple Silicon) | macos-latest |
x86_64-pc-windows-msvc | Windows (x64) | windows-latest |
aarch64-pc-windows-msvc | Windows (ARM64) | windows-11-arm |
15. Snapshot policy
- Insta
.snapfiles are committed to the repository. - Pending snapshots (
.snap.new) are gitignored. - Update workflow:
cargo insta test→cargo insta review→ commit accepted.snapfiles.
Roadmap
Phase 1 - Core & MVP symbols [COMPLETE]
Goals:
- Parser lifecycle implementation for all initial languages: Python, JavaScript, TypeScript, TSX, C, C++, Rust, Go.
- Symbol extraction and normalized IR.
- Structured JSON/YAML output (
funcs,classes,objects).
Exit gates:
- All target languages parse on fixtures.
- Stable JSON output for representative projects.
- Contract tests for required keys pass.
Phase 2 - Dependency graph & SCC [COMPLETE]
Goals:
- Build directed dependency/reference graph.
- Compute SCCs and annotate Deployment Units (independent vs. co-deployment required).
Exit gates:
- SCC results match fixture expectations.
- Cross-file dependency mapping validated on mixed-language samples.
- ReferenceEdges appear in graph output with confidence scores in cross-file resolution tests.
Phase 3 - Datagraph & optional sink [COMPLETE]
Goals:
- Extend model with optional data/flow nodes (DataNode, FlowEdge, DataScope, FlowKind).
- Implement intra-procedural def-use extraction for Rust (let bindings, parameters).
- Provide portable graph export contract with schema versioning (v1).
- Pluggable sink adapters (GraphSink trait + JsonSink).
- CLI integration:
--datagraphflag on graph subcommand. - Unified GraphOutput serialization (replaces separate datagraph module).
Exit gates:
- Export format validated via integration tests (JSON roundtrip, field checks).
- Snapshot/version semantics documented and tested (SCHEMA_VERSION = 2).
- End-to-end pipeline extracts data nodes from real Rust fixtures.
- Flow edges created for def-use chains (param→usage, let→let shadowing).
Phase 4 - CLI polish, output formats, visualization [COMPLETE]
Goals:
- Structured output (JSON + YAML) with
--formatflag. - Interactive HTML dashboard with Cytoscape.js via
--htmlflag. - Watch mode and incremental-update strategy.
- C ABI scaffolding and header generation (scoped out, see below).
Exit gates:
DONE--format json|yamlworks for analysis output.DONE--htmlgenerates a dashboard with SCC/Deployment Unit coloring, auto-opens in browser.Watch-mode stability tests pass.DONEIncremental performance target evidence captured.DONE- C ABI smoke tests. DROPPED - issue #21 closed NOT_PLANNED; the C ABI interface was proposed in RFC 0011 but not implemented. The exit gate is removed from scope and tracked as post-GSoC future work in issue #63.
Phase 5 - MetaCall Deploy Manifests [COMPLETE]
Requires --features metacall-deploy. Full documentation in DEPLOY.md.
Goals:
- Implement cross-language call-site detection across all 9 supported language ports
(
metacall_load_from_file,metacall_load_from_memory,metacall_load_from_package,metacall_load_from_configuration), including CommonJSrequire()for JS/TS and bare-name call detection for Rust afteruseimport. - Partition files into same-language pods via Union-Find over dependency edges.
- Resolve external dependencies per-language from lockfiles (preferred for exact version pinning) and package manifests (fallback).
- Generate pod manifest (
metacall.pods.json) with per-pod deployments, inter-pod edges with fused confidence scores, and scoped dependency lists. - Emit mesh annotation (
metacall.mesh.json) from SCC deployment unit analysis, classifying independent Function Mesh candidates vs. co-deployment-required groups with cross-language call-site attribution. - Implement
--checkvalidation mode: fairness check ensuring every cut edge has a corresponding RPC stub entry in the manifest (bijection check, ADR 0003 pattern).
Exit gates:
- Pod manifests generated match expected fixtures for all projects in
tests/fixtures/mixed/. DONE - Mesh annotation correctly classifies deployment units for
auth-function-meshfixture with call-site attribution. DONE --checkdetects missing RPC stubs for cut edges and reports structured diagnostics. DONE- Dynamic call-site arguments emit low-confidence annotation rather than hard failure. DONE
- External dependency resolution identifies
jsonwebtokenfrompackage.json/lockfile in theauth-function-meshfixture with exact version pinning. DONE
Phase 6 - Language expansion [COMPLETE]
Goals:
- Extend language support beyond the initial 8, prioritizing C# and Java.
- Each new language requires: grammar crate, query pack (symbols + imports + references), import resolver, visibility rules, and fixture tests.
- Cross-language Call Site detection extended to new language ports as they ship.
Outcome:
- Ruby shipped end to end: grammar, query pack, resolver, visibility rules,
fixtures, snapshots, and
metacall-deploycall-site and lockfile coverage. - C# (issue #23) and Java (issue #24) were evaluated and closed NOT_PLANNED. Ruby was the third language added, bringing the catalog to nine.
Exit gates:
- New language parses on fixtures. DONE (Ruby)
- New language pack passes extraction and cross-file dependency tests. DONE
metacall-deployfeature detects call sites in the new port bindings. DONE
Phase 7 - Validation and delivery [COMPLETE]
Goals:
- CI/CD hardening.
- Documentation completion.
- Benchmark and portability evidence.
Exit gates:
- Green CI matrix on Linux/macOS/Windows. DONE
- Benchmarks and docs published. DONE - see BENCHMARKS.md and the mdbook site (GitHub Pages).
- Candidate demo narrative aligns with delivered artifacts. DONE - see DEMO.md.
- Release artifacts (binaries, crates) published and verified. DONE - v0.5.0 on GitHub Releases (7 targets x core + deploy binaries) and crates.io.
- Release announcement drafted and scheduled. DONE - v0.5.0 release notes and the Final Report.
Phase 8 - Polyglot LSP Server & Shard Indexing (metacall/lsp) [IN PROGRESS]
Goals:
- Implement Phase 0 engine prerequisites: symbol coordinates (
source_range,file_path), in-memory buffer extraction seam (extract_text_with_id_gen), and modular.metastv2 shard and index persistence (ShardFile,ShardEdge,ShardManifestRecord,ShardHeader). - Implement dynamic cache invalidation across all import resolvers (
clear_cache). - Enable downstream
metacall/lspdevelopment for single-language and polyglot navigation:- Phase 8a: Synchronous language server (goto-definition, hover, diagnostics).
- Phase 8b: Cross-language jump-to-definition and reference resolution over
metacall()boundaries. - Phase 8c: Signature enrichment and cross-language stub generation.
Exit gates:
- Phase 0 engine seams implemented, tested, and schema version bumped to 2. DONE
.metastv2 modular shards, headers, and manifest files persist and restore graph topology. DONE- Resolver cache invalidation handles dynamic configuration updates. DONE
metacall/lsplanguage server crate operational againstmeta-astcore library.
Phase 9 - Engine Refactoring & Graph Reuse [PLANNED]
Goals:
- Zero-allocation resolver dispatch: replace
Box<dyn ImportResolver>trait objects with an enum dispatch model (Resolver) to eliminate heap allocation during pipeline runs (issue #41). - Language module deduplication: introduce declarative macros (
define_language_pack!) to eliminate repetitive spec and query boilerplate across language packs (issue #39). - Deploy pipeline modularization: extract
DeployOrchestratorstruct fromrun_deployfor single-responsibility and independent step reuse by downstream tools (issue #40). - Reusable graph visitor interfaces over
CodeGraphfor custom static analysis passes.
Exit gates:
- Zero heap allocations during per-file import resolution dispatch.
- Language pack boilerplate reduced across Python, Ruby, C, C++, Rust, Go, JS, TS, and TSX.
DeployOrchestratorexposes individual pipeline stages (scan, partition, cuts, manifests, mesh).
Phase 10 - Polyglot Security & Taint Flow Analysis (SAST) [PLANNED]
Goals:
- Deliver cross-language taint-flow analysis across MetaCall FFI boundaries (issue #29,
metacall/polyglot-sast). - Detect untrusted inputs in one language reaching dangerous execution sinks in another language.
- Classify findings into Common Weakness Enumeration (CWE) categories.
- Output native SARIF (v2.1.0) reports for GitHub/GitLab Security tab integration.
- Integrate with MetaSSR as deployment-blocking middleware and dashboard visualization.
Exit gates:
- Cross-language taint flow correctly traces from Python/JS inputs into C/Rust sinks.
- Deterministic rule-based engine emits valid SARIF v2.1.0 reports.
- MetaSSR deploy middleware blocks deployments with critical security findings.
Phase 11 - Developer Ecosystem & Community Tooling [IN PROGRESS]
Goals:
- Cross-platform distribution scripts: Unix
scripts/install.sh(issue #46) and Windowsscripts/install.ps1. - Property-based testing with
proptestfor Tarjan SCC, cycle detection, and edge normalization invariants (issue #48). - Streamline contributor experience: curated “Good First Issues” with detailed task guides.
- CLI output ergonomics: JSON error reporting and enhanced diagnostic formatting (issue #47).
Exit gates:
- Verified curl/PowerShell installation scripts published for all release artifacts.
proptestsuites validating graph normalization and SCC determinism.- Active contributor onboarding through structured issue templates.
Phase 12 - Deep Expression AST & Full Syntax Trees [PLANNED]
Goals:
- Extend
meta-astbeyond coarse symbol-level IR into fine-grained expression syntax trees and intra-procedural Control Flow Graphs (CFG). - Extract statement nodes, binary operations, control flow branches, and expression terms across all 9 supported languages.
- Maintain a layered representation:
- Layer 1 (Default): Fast, lightweight symbol & reference graph.
- Layer 2 (Opt-in): Full expression-level AST with lexical scopes and operator nodes.
- Generate intra-procedural CFGs for abstract interpretation, dead branch elimination, and fine-grained taint propagation.
Exit gates:
- Full expression AST extractable via
--depth fullorextract_full_ast. - Control Flow Graph (CFG) generated with branch conditions and join nodes.
- Zero performance regression on default symbol-only extraction passes.
Phase 13 - Polyglot Code Transformation & Refactoring Engine [PLANNED]
Goals:
- Evolve
meta-astfrom a read-only static analyzer into a bidirectional polyglot code transformation and refactoring engine. - Implement lossless Concrete Syntax Tree (CST) rewriting, preserving whitespace, formatting, and comments.
- Deliver cross-language atomic symbol renaming:
- Renaming a function or method in C, C++, or Rust automatically rewrites and updates all cross-language caller sites in Python, JavaScript, and Ruby.
- Implement automated polyglot code migrations, AST rewrite recipes, and FFI/RPC stub generation
(
meta-ast refactor,meta-ast codegen). - Provide programmatic transformation APIs for language migration tools and automated refactorings.
Exit gates:
- Lossless round-trip source rewriting verified across all 9 languages without formatting loss.
- Cross-language atomic symbol renaming verified on mixed Python/JS/Rust/C fixture codebases.
- Automated refactoring CLI (
meta-ast refactor) and FFI stub generator (meta-ast codegen).
Strategic Architecture Evolution
meta-ast follows a phased strategic evolution from lightweight symbol graph to a full polyglot
transformation engine:
- Current Foundation (Phases 1-11):
- High-speed, read-only static analysis and symbol-level IR.
- Cross-language dependency graph, import resolution, and Tarjan SCC cycle detection.
- Language Server (LSP) seams, shard index persistence (
.metastv2), and security analysis (SAST).
- Deep Syntax Expansion (Phase 12):
- Full expression-level syntax trees and Control Flow Graphs (CFG) layered over the symbol graph.
- Bidirectional Transformation (Phase 13):
- Lossless CST source rewriting, cross-language atomic refactoring, and automated code generation.
Scope boundaries
- Core priority: general-purpose symbol extraction, cross-language dependency graph, cycle detection, shard persistence, zero-cost abstractions.
- Tooling priority: Polyglot LSP server (
metacall/lsp), IDE integration, general-purpose CI gates. - Evolution priority: Full expression AST (Phase 12), Polyglot code transformation & refactoring (Phase 13), SAST security analysis (
metacall/polyglot-sast).
Crate Decisions
1. Decision principles
- Correctness and stability over novelty.
- Keep runtime dependencies minimal for CLI/library users.
- Use ecosystem-standard crates with strong maintenance signals.
2. Selected crates by concern
Parsing
tree-sitter+ language crates (c,cpp,python,javascript,typescript,rust,go)- Rationale: robust incremental parsing and grammar-level extraction.
- Language crates provide battle-tested queries and node definitions.
python,javascript,typescriptas a start in every iteration.
Graph and SCC
petgraph- Rationale: mature directed graph algorithms and built-in Tarjan SCC.
Serialization
serde,serde_json,yaml_serde- Rationale: stable, standard JSON and YAML contract tooling.
CLI and watch
clap,notify,notify-debouncer-mini,blake3- Rationale: battle-tested CLI ergonomics with color, derive, and env support.
notifyandnotify-debouncer-miniprovide OS file events for watch mode.blake3provides fast, deterministic cryptographic content hashing for file change fingerprinting.
Parallelism
rayon- Rationale: data-parallel file processing with work-stealing. A thread-local pool of
Parserinstances (thread_local!) enables safe parallel parse + extract per-file without non-Syncparser contention.
Enum utilities
strum- Rationale: derive macros for Display, EnumIter, EnumString on enums.
Filesystem
ignore,dunce- Rationale: gitignore-aware file walking (respecting .gitignore and .ignore files) and cross-platform path canonicalization.
Browser
webbrowser- Rationale: auto-open HTML dashboard in the user’s default browser.
Error handling
thiserror(library errors),anyhow(application boundary)- Rationale: explicit typed errors + practical context propagation.
3. Development dependencies
insta- snapshot testing for JSON output contracts.criterion- benchmark gating (pipeline,graph, andincremental).tempfile- isolated filesystem fixtures for integration testing.
4. Feature flags
watch- debounced file-system watch mode with incremental re-analysis and BLAKE3 fingerprinting.dataflow- data/flow node tracking and def-use graph extraction.metacall-deploy- includes deploy scanner/manifest/mesh generators for MetaCall deployment manifest generation.
5. Recommended additions
tracing,tracing-subscriber- structured observability.cbindgen- C ABI header generation when ABI phase begins.
6. Alternatives and trade-offs
- Graph: custom adjacency maps can be faster but increase maintenance cost.
- CLI: smaller parsers reduce binary size but lose feature depth.
- JSON: high-performance serializers are unnecessary before proven bottleneck.
- Parallelism: crossbeam scopes are an alternative but rayon’s work-stealing is better suited for file-level data parallelism.
- Language dispatch: trait objects allow runtime plugins but lose compile-time completeness checking; enum dispatch chosen (see
STRUCTURE.md).
7. Risk register
- Grammar drift risk (low): mitigate with fixtures + snapshots.
- Watch-mode debounce edge cases (low): mitigate with integration tests.
- Over-scoping optional sinks (medium): keep feature-gated.
8. Policy
Crate upgrades that affect behavior must include:
- CI pass on all platforms.
- Snapshot/fixture update.
- Documentation update in this file and
specs/symbol-extraction.md.
Benchmarks
Measured with criterion on the CI
runner (ubuntu-latest) and on a local developer machine. Results below are
the local run from 2026-08-03, machine: x86_64 Linux, release profile,
--features watch.
Raw criterion reports are uploaded as CI artifacts from
.github/workflows/benchmark.yml. To reproduce locally:
cargo bench --features watch
Pipeline (extraction)
End-to-end extraction across the per-language fixture suites:
| Benchmark | Time |
|---|---|
| extract/python_fixtures | 137 us |
| extract/javascript_fixtures | 11.5 ms |
| extract/rust_fixtures | 14.1 ms |
| extract/go_fixtures | 126 us |
| extract/c_fixtures | 113 us |
| extract/cpp_fixtures | 173 us |
| extract/typescript_fixtures | 121 us |
| extract/tsx_fixtures | 165 us |
| extract/mixed_fixtures | 459 us |
| extract/all_fixtures | 16.8 ms |
Graph
Graph construction, Tarjan SCC, edge deduplication, and node lookup at scale:
| Benchmark | Time |
|---|---|
| graph_construction_linear/10 | 3.6 us |
| graph_construction_linear/100 | 40.8 us |
| graph_construction_linear/1000 | 425 us |
| scc_acyclic_chain/1000 | 157 us |
| scc_single_cycle/1000 | 54 us |
| scc_multiple_cycles/100_cycles | 15.2 us |
| scc_dense_graph/200 | 356 us |
| edge_deduplication/10000_duplicates | 486 us |
| node_lookup/10000 | 9.5 us |
| full_pipeline/python_extraction_to_scc | 149 us |
| ownership_graph_only/5000 | 1.03 ms |
Datagraph and dataflow
| Benchmark | Time |
|---|---|
| datagraph_export/1000 | 325 us |
| datagraph_pipeline/python_to_datagraph | 155 us |
| dataflow_nodes_edges/5000 | 344 us |
Incremental re-analysis
Requires --features watch. Cold analyze_graph vs warm
incremental_reanalyze after a single-file change (FR-5 target: <100 ms for
files under 5k LOC):
| Benchmark | Time |
|---|---|
| incremental/cold_analyze_graph | 1.66 ms |
| incremental/warm_incremental_reanalyze_single_change | 1.16 ms |
Demo
This page walks the delivered artifacts end to end. The animated GIFs below were recorded from the real CLI against the fixture trees in this repository. Each GIF shows the command being typed and its actual output.
Intro
Version, help, and a first inspect run over the Python fixtures:
Graph
Cross-language dependency graph with SCC analysis over the
tests/fixtures/mixed/three_lang_math project (Python orchestrator, JS and
Rust workers), first as JSON then as YAML:
Deploy
metacall-deploy manifest generation for the same project, producing
metacall.pods.json and metacall.mesh.json:
Watch mode
Debounced incremental re-analysis: the watch loop prints a fresh graph
snapshot on every tick. An edit to main.py adds a function, and the next
snapshot picks up the new symbol (node_count grows, snapshot_id
increments) without a full re-parse of unchanged files:
Try it yourself
git clone https://github.com/metacall/meta-ast.git
cd meta-ast
cargo build --release --all-features
./target/release/meta-ast inspect tests/fixtures/python
./target/release/meta-ast graph tests/fixtures/mixed/three_lang_math --html
./target/release/meta-ast deploy tests/fixtures/mixed/three_lang_math --out ./deploy-out
Final Report: meta-ast, GSoC 2026
- Author: Khaled Alam
- Organization: MetaCall
- Project:
meta-ast- Standalone Polyglot Static Analysis Engine - Program: Google Summer of Code (GSoC) 2026
- Status: Completed, v0.5.0
1. Project Summary and Goals
meta-ast is a fast, standalone static analysis engine written in Rust. The project parses source code across nine programming languages, extracts a normalized symbol Intermediate Representation (IR), builds cross-language dependency graphs, detects import cycles with Tarjan Strongly Connected Components (SCC), and generates deployment manifests for the MetaCall Function Mesh runtime. The engine never executes target code.
Original Problem Statement
MetaCall supports polyglot architectures where functions written in different languages call each other seamlessly. However, developers lacked a fast, unified static analysis tool to:
- Map cross-language dependencies without running arbitrary user code.
- Detect cyclic imports that block deployment decomposition.
- Automatically partition polyglot applications into optimal, language-specific deployment pods.
- Pin external package dependencies across multiple language package managers.
Project Goals
- Polyglot Parsing: Parse 9 languages (Python, JavaScript, TypeScript, TSX, C, C++, Rust, Go, Ruby) using a unified tree-sitter pipeline.
- Normalized Symbol IR: Extract functions, classes, methods, structs, enums, and interfaces into a language-agnostic intermediate representation.
- Cross-Language Dependency Graph: Resolve imports, symbol references, and cross-language call sites with confidence-weighted edges.
- Cycle Detection and Pod Partitioning: Identify cyclic clusters using Tarjan SCC and partition code into same-language deployment units.
- MetaCall Manifest Generation: Scan cross-language
metacall_load_from_*andmetacall()invocations, generate pod manifests (metacall.pods.json) and mesh annotations (metacall.mesh.json), and validate cut fairness. - High-Performance Watch Mode: Deliver sub-100 ms incremental re-analysis using cryptographic content hashing and zero-allocation cache reuse.
- Production Quality: Provide comprehensive test coverage, robust CI/CD, cross-platform release binaries, and complete documentation.
2. What Was Accomplished (Phase-by-Phase)
The project executed across seven planned phases. All milestones were delivered, tested, and released.
Phase 1: Unified Parser Lifecycle and Symbol Extraction
- Built thread-local tree-sitter parser pools for zero-overhead multi-threaded parsing.
- Implemented uniform AST query packs across Python, JavaScript, TypeScript, TSX, C, C++, Rust, and Go.
- Normalized declarations into the
SymbolIR with visibility, signature, and docstring metadata. - Implemented robust error recovery: malformed source files emit structured
Diagnosticrecords without stopping the pipeline.
Phase 2: Dependency Graph and Tarjan SCC
- Implemented the
CodeGraphdirected graph model over petgraph. - Implemented cross-file import and symbol resolution with confidence scoring (1.0 for own file / direct import, 0.8 for transitive import, 0.6 for cross-language).
- Integrated Tarjan Strongly Connected Components (SCC) algorithm with
EdgeFilteredviews to isolate cyclic clusters while excluding ownership and flow edges. - Classified graph components into independent deployment units vs. co-deployment clusters.
Phase 3: Datagraph and Dataflow Sinks
- Extended the IR with intra-procedural def-use dataflow nodes (
DataNode,FlowEdge,DataScope). - Implemented def-use extraction for parameter bindings and variable declarations.
- Defined a schema-versioned export format (schema version 1) and the
GraphSinkpluggable adapter trait. - Added the
--datagraphCLI flag for dataflow export.
Phase 4: CLI Polish, Visualization, and Incremental Watch Mode
- Added structured output formats (
--format json|yaml) across all subcommands. - Implemented an interactive Cytoscape.js HTML visualization dashboard (
--html). - Built incremental watch mode (
--watch):- File-system monitoring with debounced event processing.
- BLAKE3 cryptographic content hashing to detect modified files.
- Zero-allocation cache reuse for unchanged files via
Arc<FileExtraction>. - Collision-free ID generation using
IdGenerator::with_start.
Phase 5: MetaCall Deployment Manifest Generator (metacall-deploy)
- Implemented AST scanners for MetaCall call sites (
metacall_load_from_file,metacall_load_from_memory,metacall_load_from_package,metacall_load_from_configuration, andmetacall()client calls per RFC 0011). - Implemented same-language pod partitioning using Union-Find over resolved dependency edges.
- Added external package dependency resolution from lockfiles (
package-lock.json,Cargo.lock,go.sum,requirements.txt,Gemfile.lock) for exact version pinning. - Generated deployment artifacts:
metacall.pods.json: Pod manifests with deployment units, dependency lists, and AST metrics.metacall.mesh.json: Function Mesh topology annotations with cross-language edge attribution.
- Added
--checkmode: verifies cut fairness (every cut edge across pods has a corresponding RPC stub entry, fulfilling ADR 0003).
Phase 6: Language Expansion (Ruby)
- Implemented full Ruby support: tree-sitter grammar integration, symbol query pack,
require/require_relativeimport resolver, reference detection, and lockfile resolution (Gemfile.lock). - Evaluated C# and Java; concluded Ruby provided the highest immediate utility for MetaCall Function Mesh targets.
Phase 7: Validation, CI/CD, Benchmarking, and Delivery
- Hardened CI matrix across 4 operating systems (Linux glibc/musl, macOS x86_64/ARM64, Windows x86_64/ARM64) on stable and nightly Rust toolchains.
- Added automated linting, formatting, cargo-deny license/vulnerability audits, and cargo-nextest execution.
- Configured Criterion benchmark suite tracking pipeline, graph, and incremental performance.
- Published mdbook documentation to GitHub Pages.
- Published crate releases to crates.io and multi-platform binaries to GitHub Releases.
3. Code Merged Upstream
All work was developed in pull requests, reviewed, and merged into the main branch of metacall/meta-ast.
Merged Pull Requests
| PR | Title | Description |
|---|---|---|
| #1 | Skeleton | Initial project structure, tree-sitter integration, base CLI. |
| #3 | Refactor - 1 | Parser lifecycle and normalized symbol data model. |
| #4 | Ref2 | Extractor modularization and multi-language query packs. |
| #5 | Phase2/dep-graph | Dependency graph construction, petgraph integration, Tarjan SCC. |
| #6 | Unify Serialization and Introduce HTML Dashboard | JSON/YAML emitters, Cytoscape.js interactive visualization. |
| #7 | Cross-file dependency mapping (RFC 0009) | Cross-file import and reference resolution with confidence scoring. |
| #8 | Scope wrapper enhancements | Symbol lookup scoping and namespace qualification. |
| #9 | Combined query optimization | Consolidated AST queries for improved parsing throughput. |
| #10 | Merge CI workflows | Unified CI pipeline (build, test, lint, deny, fmt). |
| #11 | Group and trace | Traceability matrices and graph node groupings. |
| #12 | Deep graph assembly | Edge normalization, confidence fusion, and diagnostics propagation. |
| #13 | Deploy documentation | Specifications and user guides for deployment manifest generation. |
| #14 | Deploy module foundation | Call-site scanner and initial manifest generator. |
| #16 | Architecture and ADRs update | Documentation of ADR 0001-0004 and RFC 0001-0010. |
| #17 | Complete MetaCall deployment manifest generator | Pod partitioning, lockfile resolution, mesh annotation, --check mode. |
| #30 | Optimization 1 | Parser reuse and memory allocation optimizations. |
| #31 | Phase 3 datagraph optional sink | Dataflow IR, def-use analysis, pluggable sink trait, --datagraph. |
| #32 | ID generation optimization | Atomic ID generation and newtype validation. |
| #33 | Watch mode and incremental update strategy | Debounced watcher, BLAKE3 change detection, incremental re-analysis. |
| #34 | Add Ruby support | Ruby grammar, symbol queries, import resolver, lockfile parsing. |
| #35 | Phase 7 completion | mdbook documentation site, benchmark suites, demo recordings. |
| #36 | Release v0.5.0 | Release automation, packaging, and announcements. |
| #37 | GitHub Pages deployment workflow | Automated mdbook deployment to GitHub Pages. |
| #53 | CLI & graph hardening | --language filtering, --max-pod-size, O(1) edge normalization. |
Notes
- there was multiple commits unlinked with issues or PRs “Mostly optimizations”.
4. Key Metrics and Project Numbers
- Languages Supported: 9 languages (Python, JavaScript, TypeScript, TSX, C, C++, Rust, Go, Ruby).
- Test Suite: 430+ automated tests (327 unit tests, 106 integration tests, doc tests).
- Test Matrix: 4 Operating Systems (Linux, macOS, Windows, Windows ARM) across 2 Rust toolchains (stable and nightly).
- Performance Benchmark:
- Incremental warm re-analysis: 1.16 ms for single-file changes (target was <100 ms).
- Graph construction & Tarjan SCC: <0.5 ms for 1,000 nodes.
- End-to-end extraction across fixture suite: 16.8 ms.
- Releases:
- 5 GitHub releases (
v0.1.0throughv0.5.0). - 14 pre-compiled binary packages per release (covering Linux glibc/musl, macOS x86/ARM, Windows x86/ARM across core and deploy configurations).
- 2 published crates.io versions (
0.4.0,0.5.0).
- 5 GitHub releases (
5. Technical Challenges and Engineering Insights
A. Polyglot Semantic Gap and Import Resolution
Challenge: Each programming language uses different module and symbol resolution semantics. C/C++ relies on header inclusion; Python uses runtime sys.path and relative imports; JavaScript/TypeScript uses ESM, CommonJS, and tsconfig.json path mappings; Rust uses hierarchical crate paths; Ruby uses require and require_relative.
Solution: Designed the ImportResolver abstraction. Each language provides a stateful resolver that caches directory hierarchies and configuration files. Unresolved imports are recorded with confidence penalties (0.6 cross-language vs 1.0 same-file) and surface as non-fatal diagnostics.
B. Collision-Free Incremental State in Watch Mode
Challenge: When re-analyzing modified files in watch mode, creating new symbol IDs could collide with cached IDs from unchanged files or force expensive whole-graph re-allocations.
Solution: Implemented an explicit ID generation seam using IdGenerator::with_start(max_cached_id + 1). Unchanged files retain their existing Arc<FileExtraction> references with zero allocations (verified via Arc::ptr_eq), while newly extracted files receive strictly monotonic, non-overlapping IDs.
C. Deployment Cut Fairness and Invariant Verification
Challenge: Partitioning polyglot applications into separate deployment pods can sever dependencies. If an edge across pods lacks an RPC stub, the deployed application fails at runtime.
Solution: Implemented the cut fairness validation algorithm (ADR 0003). In --check mode, the analyzer constructs a bijection between inter-pod cut edges and declared RPC stubs. Any missing stub produces an immediate diagnostic error with the exact source location of the call site.
D. Graph Assembly Performance at Scale
Challenge: Initial graph construction used repeated edge scans to deduplicate multi-language references, causing quadratic slowdowns on large graphs.
Solution: Replaced linear scans with an indexed (src, dst, kind) lookup map in CodeGraph, delivering O(1) edge deduplication and max-confidence fusion. This reduced graph construction time for 10,000 duplicate edges to 486 microseconds.
E. Cross-Platform Path Normalization
Challenge: File paths and snapshots differed across Linux, macOS, and Windows due to backslashes and case sensitivity. Solution: Enforced universal forward-slash path normalization across all internal data structures, JSON serializers, and snapshot tests, ensuring deterministic CI verification across all four operating systems.
6. How to Build, Test, and Verify
Prerequisites
- Rust 1.94.0 or newer (rustup.rs)
Build from Source
git clone https://github.com/metacall/meta-ast.git
cd meta-ast
# Build core analyzer
cargo build --release
# Build with deployment manifest generator and watch mode
cargo build --release --features metacall-deploy --features watch --features dataflow
Run Test Suite
# Run all tests across all feature flags
cargo test --all-features
# Run linter and formatting checks
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --check
Run Benchmarks
cargo bench --features watch
Run CLI Commands
# Inspect declarations in a project
./target/release/meta-ast inspect ./tests/fixtures/python/ -f json
# Build graph with interactive HTML dashboard
./target/release/meta-ast graph ./tests/fixtures/mixed/ --html -o dashboard.html
# Generate MetaCall deployment manifests and check cut fairness
./target/release/meta-ast deploy ./tests/fixtures/mixed/auth_microservice --check -o ./deploy_out
7. Current State and Future Work
Current State
meta-ast is feature-complete for its GSoC 2026 milestones and project goals. The engine is released as v0.5.0 on crates.io and GitHub Releases, with documentation published at metacall.github.io/meta-ast.
Future Work (Post-GSoC Roadmap)
- C ABI and Header Generation: Implement automatic C header generation for exported polyglot functions (RFC 0011).
- Deeper Intra-Procedural Dataflow: Expand dataflow node extraction from Rust to JavaScript, TypeScript, Python, and Go.
- Additional Language Packs: Add grammars for PHP, Java, and C# based on user demand.
- Polyglot SAST Integration: Integrate static application security testing rules and machine learning assisted anomaly detection (RFC 0029).
8. Important Links
- Source Code Repository: https://github.com/metacall/meta-ast
- crates.io Package: https://crates.io/crates/meta-ast
- Documentation Book: https://metacall.github.io/meta-ast/
- GitHub Releases & Binaries: https://github.com/metacall/meta-ast/releases
- Architecture Overview: docs/src/ARCHITECTURE.md
- Deployment Manifest Specification: docs/src/DEPLOY.md
- Benchmark Suite: docs/src/BENCHMARKS.md
- Recorded Demonstrations: docs/src/DEMO.md
Requirements Specification
1. Purpose
Define normative requirements for meta-ast, a standalone Rust static analyzer for
polyglot source trees. The tool extracts symbol surfaces, builds cross-file dependency
graphs, and computes SCCs. MetaCall FaaS deployment manifest generation is an
optional capability behind the metacall-deploy feature flag.
2. Functional requirements
FR-1: Parsing and language support
The analyzer shall parse source files for a growing set of languages, starting with:
- Python
- JavaScript
- TypeScript (including TSX)
- C
- C++
- Rust
- Go
Additional languages (C#, Java, and others) are planned in later phases.
FR-2: Symbol extraction
The analyzer shall extract top-level symbols and language-appropriate nested symbols into a normalized intermediate representation:
- Functions
- Classes / structs / interfaces / traits
- Objects (constants, globals, module-level bindings)
FR-3: Dependency graph
The analyzer shall construct a directed graph of symbol-level dependencies:
- File import/usage edges
- Intra-project symbol references
- Cross-language reference candidates
FR-4: SCC and Deployment Unit identification
The analyzer shall compute Strongly Connected Components (Tarjan) and annotate each SCC as a Deployment Unit, classifying it as:
- Independent (acyclic, Function Mesh separation candidate)
- Co-deployment required (cyclic, must remain grouped)
FR-5: Incremental updates (Planned)
The analyzer is designed to support update workflows from file changes with a target incremental response of under 100ms for files below 5k LOC. Note: This is planned for Phase 4 of the roadmap and is not yet implemented.
FR-6: CLI and library modes
The project shall expose:
- Rust library interface
- CLI entrypoint for project analysis and output emission
FR-7: C ABI (Planned)
The project will provide a stable C ABI header (mc_ast.h) for embedding scenarios in a later phase.
FR-8: Datagraph export (Planned)
The project will support exporting a datagraph model suitable for external graph sinks (including Dgraph) in a later phase.
FR-9: Deploy Manifest generation (feature-gated: metacall-deploy, Implemented)
When built with --features metacall-deploy, the deploy subcommand:
- Scans source files for cross-language call sites
(
metacall_load_from_file,metacall_load_from_memory,metacall_load_from_package,metacall_load_from_configuration) - Extracts
(language_tag, script_paths[])pairs from call-site arguments - Partitions files into same-language pods via Union-Find over dependency edges
- Resolves external dependencies per-language from lockfiles and package manifests
- Generates a pod manifest (
metacall.pods.json) with per-pod deployments, inter-pod edges with fused confidence scores, and scoped dependency lists with pinned versions - Inlines referenced
metacall_load_from_configurationtargets when present; emits low-confidence annotations for computed/dynamic arguments - Runs fairness checks when
--checkis passed: every cut edge must have a corresponding RPC stub entry in the manifest
FR-10: Mesh Annotation (feature-gated: metacall-deploy, Implemented)
When built with --features metacall-deploy, the deploy subcommand also emits
metacall.mesh.json containing:
- SCC-derived deployment units with constituent symbol lists
- Cross-language boundary flags per unit
- Independent mesh candidate classification per unit
- Cross-language edges with call-site file attribution
3. Non-functional requirements
NFR-1: Correctness
Incorrect symbol labeling is a high-severity defect. Correctness takes priority over throughput.
NFR-2: Resilience
Malformed source files shall not crash analysis. Partial extraction shall be allowed when parser recovery is possible. Unresolvable Cross-Language Call Site arguments (dynamic values) shall be annotated, not silently discarded.
NFR-3: Portability
Build and test shall pass on Linux, macOS, and Windows.
NFR-4: Determinism
Given identical input set and tool version, emitted output shall be deterministic.
NFR-5: Observability
The implementation shall provide structured diagnostics suitable for CI and local debugging.
4. Acceptance criteria
- Parse all supported languages and emit valid symbol JSON.
- Correct SCC identification for multi-language project fixtures.
- CI pipeline green on Linux/macOS/Windows.
- (Planned) Incremental-update target under 100ms for files below 5k LOC.
- (Planned with
metacall-deploy) Deploy Manifests generated match expected fixtures for all example projects intests/fixtures/mixed/. - (Planned with
metacall-deploy) Mesh Annotation correctly classifies Deployment Units for theauth-function-meshfixture.
5. Out-of-scope for MVP
- Incremental watch-mode and C ABI embedding (post-MVP).
metacall-deploymanifest/mesh generation and--checkmode (post-MVP).- Dgraph sink/export adapter (post-MVP).
- Full inter-procedural global dataflow with alias analysis.
- Sound cross-language type inference.
- Mandatory online graph database dependency.
- Dynamic Cross-Language Call Site resolution (runtime tag/path values).
6. Versioning policy
Breaking changes to output schema or graph semantics require:
- ADR update.
- Traceability matrix update.
- Migration note in roadmap/changelog.
Graph Model Specification
1. Purpose
Define the normalized graph model used for dependency analysis and SCC computation.
2. Node categories
FileNode
id: stable identifierpath: normalized path relative to the analyzed project rootlanguage_id: configured language/runtime identifiersnapshot_id: snapshot identifier for the analysis version this file belongs to
SymbolNode
id: stable identifiername: symbol namekind: source symbol category such as function | class | object | interface | trait | struct | enum | methodfile_id: references the owningFileNode.idvisibility: optional, when applicable: public | privatesource_range: byte/line range
DataNode
id: stable identifier for a value-bearing nodesymbol_id: optional symbol reference when the data node is derived from a named symbolscope: local, parameter, closure, member, or temporary scope classificationtype_hint: optional inferred or declared type information
DataNode represents a value or variable instance used for def-use and flow analysis rather than a declaration boundary.
3. Edge categories
ImportEdge
FileNode -> FileNode representing import/include/use relationships.
ReferenceEdge
SymbolNode -> SymbolNode representing symbol usage/call/reference candidates.
OwnershipEdge
FileNode -> SymbolNode and optional SymbolNode -> SymbolNode for nesting.
FlowEdge
DataNode -> DataNode for def-use transitions.
4. Graph invariants
- Every SymbolNode must map to exactly one FileNode.
- Ownership edges must form an acyclic containment structure.
- SCC computation applies to dependency/reference subgraph, not ownership edges. Self-loop detection and independence classification follow the same subgraph rule.
- Duplicate edges should be normalized by
(src, dst, edge_kind)key. Client-call edges (FileNode to SymbolNode) can only merge with other client-call edges; scope-resolved references (SymbolNode to SymbolNode) never collide with them. Strongest evidence wins within a triple.
5. SCC semantics
Tarjan SCC runs on directed dependency/reference graph.
- SCC size = 1 with no self-loop => acyclic unit.
- SCC size > 1 or self-loop => cyclic unit.
Deployability hint policy:
- Acyclic SCCs are preferred deployment candidates.
- Cyclic SCCs require grouped deployment or refactor guidance.
6. Serialization contract
Graph serialization for external consumers shall preserve:
- snapshot-local node IDs
- edge kinds
- defining file paths and source ranges for symbol nodes
Sink adapters (e.g., Dgraph) that must preserve semantic equivalence.
The serialized language field on file and external nodes uses the canonical
lowercase language names, in enum declaration order:
python, javascript, typescript, tsx, c, cpp, rust, go, ruby
These values also parse back through the CLI --language flag. The same names
apply to the strum Display/AsRefStr and serde representations of LangId.
Graph output schema version 2 adds file_path and source_range to serialized
symbol nodes. .metast v2 shards do not persist numeric IDs. They store stable
language-scoped endpoint names and regenerate symbol IDs when loaded.
7. Known limitations
- Cross-language resolution is initially best-effort string/scope matching.
- Full semantic type equivalence across languages is deferred.
- DataNode/FlowEdge extraction is implemented for Rust (let bindings,
parameters, def-use chains) behind the
dataflowfeature flag; other languages return empty vectors. Python/JS/TS/Go/C/C++ are stubbed with TODO markers. Full coverage is tracked in Phase 6.
Symbol Extraction Specification
1. Purpose
Define language-pack extraction contracts backed by Tree-sitter grammar queries.
Each language pack covers symbols, imports, and references. The set of supported
languages grows over time; see ROADMAP.md Phase 6 for the expansion plan.
2. Shared extraction rules
- Prefer grammar field-based extraction where available.
- Record both byte and line/column ranges.
- Continue extraction in presence of parser recovery nodes when safe.
3. Language packs
Python
- Extract: functions, classes, imports, module-level assignments.
- Handle decorated definitions and async functions.
JavaScript
- Extract: function declarations, function expressions, arrow functions, classes, methods, imports/exports.
TypeScript / TSX
- Extract JS symbols plus interfaces, type aliases, enums.
- Use TSX grammar for JSX-bearing files.
C
- Extract: function definitions/declarations, structs, enums, typedefs, includes.
- Distinguish declaration vs definition ‘where possible’.
C++
- Extract C symbols plus classes, namespaces, templates, aliases, method definitions.
Rust
- Extract: functions, structs, enums, traits, impl blocks, use declarations, modules, const/static/type aliases.
Go
- Extract: functions, methods with receiver, types, interfaces, imports, const/var declarations.
4. Output normalization
Each extracted symbol maps to canonical shape:
namekindlanguagefilesource_range- optional:
signature,visibility,docstring,async
5. Error tolerance policy
- Keep partial output if recoverable parse exists.
- Emit diagnostics for query compile failures or unsupported grammar drift.
6. Version policy
Query packs are tied to grammar versions in Cargo.toml. Any grammar upgrade requires:
- Query validation pass.
- Fixture/snapshot refresh.
- Update to this spec.
Traceability Matrix
Deliverables to docs mapping
| Deliverable | Documentation source | Validation target |
|---|---|---|
| Rust lib + CLI | ARCHITECTURE.md, ROADMAP.md | build/test/CLI fixture checks |
| Language packs | specs/symbol-extraction.md | language fixture + snapshot tests |
| Dependency graph + SCC | specs/graph-model.md, rfcs/0004-graph-representation-and-scc.md, rfcs/0008-graph-module.md, rfcs/0009-cross-file-dependency-mapping.md | graph fixture SCC assertions |
| Deployment Unit annotation | specs/graph-model.md, rfcs/0004-graph-representation-and-scc.md | SCC classification fixture tests |
| Dataflow (stretch) | specs/graph-model.md, rfcs/0007-dgraph-integration-scope.md | optional feature tests |
| Dgraph sink | rfcs/0007-dgraph-integration-scope.md + specs/graph-model.md | export contract validation |
Deploy Manifests (metacall-deploy) | specs/requirements.md FR-9 | fixture manifest comparison tests |
Mesh Annotation (metacall-deploy) | specs/requirements.md FR-10 | SCC unit classification tests |
Acceptance criteria mapping
| Acceptance criterion | Spec source | Verification plan |
|---|---|---|
| Parse all supported languages | specs/requirements.md FR-1 | per-language fixture parsing tests |
| Correct SCC identification | specs/graph-model.md | graph fixture SCC assertions |
| Deployment Unit classification | specs/requirements.md FR-4 | independent vs. co-deploy fixture assertions |
| Incremental update target | ARCHITECTURE.md + rfcs/0003-incremental-parsing-strategy.md | benchmark harness and thresholds |
| Linux/macOS/Windows portability | CI_CD.md | CI matrix status |
| Deploy Manifest fixture match | specs/requirements.md FR-9 | tests/fixtures/mixed/ manifest comparison |
| Mesh Annotation correctness | specs/requirements.md FR-10 | auth-function-mesh unit classification |
Decision traceability
| Decision area | ADR |
|---|---|
| Language loading model | rfcs/0001-language-loading-model.md |
| Error semantics | rfcs/0002-error-semantics-and-recovery.md |
| Incremental strategy | rfcs/0003-incremental-parsing-strategy.md |
| Graph representation | rfcs/0004-graph-representation-and-scc.md |
| Output contract policy | rfcs/0005-output-contract-policy.md |
| Type inference scope | rfcs/0006-type-inference-scope.md |
| Dgraph scope | rfcs/0007-dgraph-integration-scope.md |
| Graph module design | rfcs/0008-graph-module.md |
| Cross-file dependency mapping | rfcs/0009-cross-file-dependency-mapping.md |
0001-stateful-import-resolver
We introduced a stateful ImportResolver trait utilizing thread-safe interior mutability (RwLock and OnceLock) for caching module boundaries and file existence checks, replacing the previous stateless function pointer resolution.
This design satisfies Rust’s thread-safety constraints (Send + Sync) to allow gradual migration of 8 languages, prepares the pipeline for parallelized import resolution under Rayon, and supports persistent cache reuse across incremental compilation runs.
0002-scope-resolution-heuristics
We resolve symbol references across files using a single, unified scope resolution strategy: transitive BFS lookup over the import graph.
Rather than maintaining separate “strict” and “heuristic” CLI flags, we build a flattened scope cache for each file. The resolver crawls the import graph using a breadth-first search (BFS). Confidence scores decay based on import distance and language boundaries:
- 1.0: Reference is local (distance = 0) or direct, same-language (distance = 1).
- 0.8: Reference is transitive, same-language (distance > 1).
- 0.6: Reference is cross-language (distance >= 1).
This keeps the CLI interface clean and guarantees security or dependency tools high recall by default, while still capturing precision through the decayed confidence scores.
0003-unresolved-import-policy
We defined a strict policy for handling unresolved imports to prevent silent failures and preserve dependency context.
Unresolved Relative Imports (specifiers starting with . or /) are treated as configuration errors and emit a warning Diagnostic containing the source range and path. Unresolved Non-Relative Imports are treated as third-party package dependencies and are mapped to a placeholder External Node in the directed graph rather than being silently discarded, preserving the complete structural architecture.
0004-global-scope-synthetic-symbols
We proposed introducing a synthetic Module Body Symbol (named "<global>" or "<module>") for files containing top-level global execution blocks to represent module-level dependencies. Under that proposed schema, references occurring outside any class, function, or method boundary would be owned by this synthetic symbol.
Current MVP Status:
We do not yet generate this synthetic symbol. In the current implementation, references that occur at the top-level (outside of functions or classes) have a None source symbol and are skipped during scope resolution.
Implementing the synthetic module body symbol is deferred to a future phase to maintain simplicity in the initial graph schema.
RFC 0001: Language Loading Model
Status
Accepted
Context
The analyzer must support multiple Tree-sitter grammars with predictable behavior and low operational complexity.
Decision
Use compile-time language crate integration with explicit language dispatch.
Alternatives considered
- Runtime-loaded grammars via dynamic libraries.
- Hybrid runtime plugin registry.
Rationale
Compile-time dispatch maximizes type safety and build determinism for MVP scope.
Consequences
- New language support requires source-level addition and release.
- Lower runtime complexity and fewer deployment surprises.
RFC 0002: Error Semantics and Recovery
Status
Accepted
Context
Static analysis should remain useful even on partially invalid source code.
Decision
Treat parse errors as recoverable when Tree-sitter yields partial trees; treat unrecoverable extraction/config errors as scoped failures with diagnostics.
Alternatives considered
- Fail-fast on first parse anomaly.
- Silent permissive mode with minimal diagnostics.
Rationale
Recoverable parsing preserves developer feedback loops while maintaining explicit failure signaling.
Consequences
- Partial results are possible and expected.
- Diagnostics become part of core product quality.
RFC 0003: Incremental Parsing Strategy
Status
Accepted
Context
Proposal target includes incremental responsiveness (<100ms for files under 5k LOC).
Decision
Adopt a staged approach:
- Correct baseline implementation first.
- Incremental optimization with
InputEditand changed-range narrowing behind benchmark evidence.
Alternatives considered
- Full incremental complexity from day one.
- Full reparse forever.
Rationale
Correctness-first minimizes early defect risk; benchmark-driven optimization avoids premature complexity.
Consequences
- Early versions may reparse more broadly.
- Optimization milestones are explicitly measurable.
RFC 0004: Graph Representation and SCC
Status
Accepted
Context
The project requires dependency analysis and SCC computation across polyglot symbol sets.
Decision
Use a directed graph representation with explicit node/edge kinds and Tarjan SCC as the canonical cycle analysis algorithm.
Alternatives considered
- Custom adjacency structures.
- Relational-first representation.
Rationale
A typed directed graph model matches analysis semantics and keeps SCC computation straightforward and testable.
Consequences
- Graph model contracts must stay stable (
specs/graph-model.md). - SCC behavior is deterministic and auditable.
RFC 0005: Output Contract Policy
Status
Accepted
Context
Compatibility with MetaCall-style inspect consumers is a central project requirement.
Decision
Make inspect-compatible JSON the primary output contract, preserving stable keys:
funcsclassesobjects
Alternatives considered
- New schema first, with adapters later.
- Multiple equal-priority formats.
Rationale
Contract stability reduces integration risk and aligns with proposal goals.
Consequences
- Breaking output changes require versioning, migration note, and traceability update.
- Snapshot/schema validation becomes mandatory in CI.
RFC 0006: Type Inference Scope
Status
Accepted
Context
Cross-language full type inference is high complexity and not required for MVP parity.
Decision
Limit MVP to extraction of declared/type-hint metadata and best-effort symbol linking. Defer sound cross-language inference to future milestones.
Alternatives considered
- Full type system in MVP.
- No type metadata at all.
Rationale
Keeps scope feasible while preserving useful metadata for analysis.
Consequences
- Some cross-language links remain heuristic.
- Future advanced inference can be introduced without breaking core contracts.
RFC 0007: Dgraph Integration Scope
Status
Accepted
Context
The proposal includes Dgraph sink capability, but standalone analyzer operation is mandatory.
Decision
Treat Dgraph integration as optional/export-layer capability. Core analysis must not depend on external graph database availability.
Alternatives considered
- Dgraph as required runtime dependency.
- No graph sink pathway.
Rationale
Optional integration preserves standalone usability and reduces operational burden in MVP.
Consequences
- Export contracts must remain sink-agnostic.
- Dgraph adapter can evolve independently behind feature boundaries.
RFC 0008:Graph Module and SCC Analysis
Status
Accepted
Context
Phase 1 of meta-ast established the foundation: file discovery, parsing, symbol extraction, and inspect-compatible JSON output. Phase 2 extends this with dependency graph construction and Strongly Connected Component (SCC) analysis to provide deployability insights for polyglot codebases.
The graph model was specified in specs/graph-model.md and the implementation approach using petgraph was validated through research. This RFC defines the concrete module structure, APIs, and implementation strategy.
Goals
- Build a directed dependency/reference graph from extracted symbols
- Implement Tarjan SCC algorithm for cycle detection
- Provide deployability hints based on SCC analysis
- Maintain stable inspect output contract (
funcs,classes,objects) - Support cross-file dependency mapping for mixed-language projects
Non-Goals
- Full inter-procedural dataflow analysis (Phase 3)
- Live graph database integration (Phase 4)
- Real-time incremental graph updates (Phase 4)
- Cross-language type inference (out of scope for MVP)
Design
1. Module Structure
New modules under src/graph/:
| File | Responsibility |
|---|---|
mod.rs | Public exports, CodeGraph struct, graph operations |
node.rs | FileNode, SymbolNode, NodeData enum |
edge.rs | EdgeKind, EdgeData with metadata |
builder.rs | GraphBuilder for incremental construction |
scc.rs | SccAnalysis, Tarjan SCC, deployability hints |
Output extension: src/output/graph.rs for graph serialization.
2. Graph Types
Node storage uses petgraph DiGraph<NodeData, EdgeData> with stable node indices. NodeData is an enum for heterogeneous node types:
#![allow(unused)]
fn main() {
pub enum NodeData {
File(FileNode),
Symbol(SymbolNode),
}
pub struct FileNode {
pub id: FileId,
pub path: PathBuf, // Project-relative as discussed with vicente
pub language_id: LangId,
pub snapshot_id: SnapshotId,
}
pub struct SymbolNode {
pub id: SymbolId,
pub name: String,
pub kind: SymbolKind,
pub file_id: FileId,
pub visibility: Option<Visibility>,
pub source_range: SourceRange,
}
}
Edges carry kind and metadata:
#![allow(unused)]
fn main() {
pub enum EdgeKind {
Import, // File imports another file
Reference, // Symbol references another symbol
Ownership, // File owns symbol, or symbol contains nested symbol
}
pub struct EdgeData {
pub kind: EdgeKind,
pub strength: EdgeStrength, // Strong, Weak, or Dynamic
}
pub enum EdgeStrength {
Strong, // Direct, resolvable dependency
Weak, // Optional or conditional
Dynamic, // Runtime-resolved
}
}
3. Graph Construction
Two-phase construction from extraction results:
Phase A - Ownership graph (always acyclic by construction):
- Add FileNode for each processed file
- Add SymbolNode for each extracted symbol
- Add Ownership edges: FileNode -> SymbolNode
Phase B - Dependency graph:
- Add Import edges: FileNode -> FileNode (cross-file imports)
- Add Reference edges: SymbolNode -> SymbolNode (symbol usage)
Import extraction extends the language pack system with import-specific tree-sitter queries per language.
4. SCC Analysis
SCC computation follows specs/graph-model.md invariants:
- SCC runs on dependency subgraph only (Import + Reference edges)
- Ownership edges are explicitly excluded from SCC computation
- Duplicate edges normalized by (src, dst, edge_kind)
Tarjan’s algorithm via petgraph::algo::tarjan_scc produces components in reverse topological order.
SccAnalysis result structure:
#![allow(unused)]
fn main() {
pub struct SccAnalysis {
pub components: Vec<Scc>,
pub node_to_component: HashMap<NodeIndex, usize>,
}
pub struct Scc {
pub index: usize,
pub nodes: Vec<NodeIndex>,
pub is_cyclic: bool,
pub deployability_hint: DeployabilityHint,
}
pub enum DeployabilityHint {
Independent, // Size=1, no self-loop
AcyclicDependency, // Size=1, depends on other components
CyclicCluster, // Size>1 or self-loop present
}
}
5. CLI Integration
New subcommand graph alongside existing inspect:
#![allow(unused)]
fn main() {
pub enum Cli {
Inspect(InspectArgs),
Graph(GraphArgs), // New
}
}
GraphArgs accepts same path/language filters as InspectArgs plus output format options.
Output format (JSON):
{
"meta": {
"snapshot_id": 1,
"file_count": 10,
"symbol_count": 150,
"edge_count": 200
},
"nodes": [
{"id": "F0", "kind": "file", "path": "src/main.py", "language": "python"},
{"id": "S42", "kind": "symbol", "name": "main", "kind": "function", "file_id": "F0"}
],
"edges": [
{"source": "F0", "target": "F1", "kind": "import"},
{"source": "S42", "target": "S43", "kind": "reference"}
],
"sccs": [
{
"index": 0,
"nodes": ["S10", "S11"],
"is_cyclic": true,
"deployability": "cyclic_cluster",
"size": 2
}
],
"deployability_report": {
"independent_units": 120,
"cyclic_clusters": 5,
"total_components": 125
}
}
6. Error Handling
Graph construction errors are recoverable and emit diagnostics:
- Missing import target: warning, edge not added
- Duplicate edge: deduplicated silently
- Cycle in ownership edges: error (violates invariant)
SCC computation is infallible for valid graphs.
7. Testing Strategy
- Unit tests for GraphBuilder with known input/output
- Unit tests for SCC computation on synthetic cyclic/acyclic graphs
- Fixture tests for cross-file import detection per language
- Integration tests for mixed-language dependency chains
- Snapshot tests for graph JSON output format
8. Performance Considerations
- Graph construction: sequential (linear in symbols + edges)
- Symbol extraction remains parallel (rayon)
- SCC computation: O(V + E) via Tarjan
- Memory: adjacency list via petgraph (O(V + E))
- No incremental updates in Phase 2 (full graph rebuild)
Migration Path
This RFC introduces new modules without breaking existing Phase 1 functionality:
inspectsubcommand unchanged- Symbol extraction interface unchanged
- New graph functionality additive only
Existing tests continue to pass. New tests validate graph-specific behavior.
Alternatives Considered
Alternative 1: Relational-first representation
Instead of adjacency list, use relational tables (Vec of nodes, Vec of edges with indices).
Rejected: petgraph provides battle-tested algorithms and the graph is inherently graph-structured. Relational adds indirection without benefit for SCC computation.
Alternative 2: Custom SCC implementation
Implement Tarjan from scratch instead of using petgraph.
Rejected: petgraph’s implementation is optimized and widely tested. No performance justification for custom implementation.
Alternative 3: Separate ownership and dependency graphs
Maintain two distinct graph structures.
Rejected: Single graph with edge kind filtering is simpler and memory-efficient. SCC explicitly filters by edge kind.
Open Questions
-
Should we include intra-file reference edges (symbol calls within same file) in SCC? yes, for completeness.
-
How to handle unresolved imports (external dependencies)? Skip with warning,? Decision: Skip with warning for Phase2.
-
Should deployability hints include suggested entry points for cyclic clusters? Deferred to Phase 3 when call graph is richer.
References
specs/graph-model.md- Graph semantics and invariantsspecs/requirements.md- FR-4, FR-5 for graph and SCC requirements- ADR 0004 - Graph representation decision record
- petgraph documentation: https://docs.rs/petgraph
RFC 0009: Cross-File Dependency Mapping
Status
Accepted
Context
The current meta-ast architecture successfully extracts definitions (functions, classes) and builds a foundational CodeGraph. However, it does not extract or resolve cross-file dependencies (imports and references) in the main pipeline. For meta-ast to serve as a robust backbone for SAST tools, it must accurately map how data and control flow across file boundaries.
This RFC defines the implementation of a graph-driven scope resolution strategy with high-performance optimizations to bridge the gap between extraction and dependency mapping.
Goals
- Extract
importstatements and symbolreferencesacross supported languages. - Resolve symbol references to their definitions using graph-aware scoping.
- Validate cross-file dependency mapping on mixed-language projects.
- Maintain high precision and performance for large-scale codebases.
5. Multi-Pass Resolution Strategy
To ensure all definitions are available before resolution, the pipeline follows four sequential passes:
- Pass 1: Extract symbols, imports, and references (Parallelizable).
- Pass 2: Build File Nodes and resolve/add
ImportEdges. - Pass 3: Build Export Map Cache (BFS/DFS with circular dependency guards).
- Pass 4: Resolve References using local scope + Export Map.
Design
1. Data Model Extensions
New Types in src/model/mod.rs:
#![allow(unused)]
fn main() {
pub struct UnresolvedImport {
pub target_path: String, // Raw path from source
pub namespace: Option<String>,
pub alias: Option<String>,
pub is_star: bool,
pub range: SourceRange,
}
pub struct UnresolvedReference {
pub source_symbol: Option<SymbolId>, // None if module-level
pub name: String,
pub range: SourceRange,
}
}
2. Extraction Pipeline Update
The ExtractionResult carries these unresolved items. Note that source_path is omitted from individual structs as it is stored at the container level.
#![allow(unused)]
fn main() {
pub struct ExtractionResult {
pub symbols: Vec<Symbol>,
pub imports: Vec<UnresolvedImport>,
pub references: Vec<UnresolvedReference>,
pub diagnostics: Vec<Diagnostic>,
}
}
Testing Strategy
- Unit Tests: Verify
GraphBuilderresolution with shadowing, circular dependencies, and transitive imports. - Integration Tests: Use
tests/fixtures/mixed/to validate cross-language resolution precision.
Key Design Decisions
1. Decoupled Extraction Identifiers
UnresolvedImport and UnresolvedReference use name-based/path-based identification during extraction. This ensures the parser remains stateless and testable in isolation from the global FileId state.
2. Graph-Driven Scope Resolution with Export Maps
To avoid the $O(R \times (V + E))$ bottleneck of naive BFS traversal, resolution uses a caching strategy:
- Export Maps: Once
ImportEdgesare established, the builder pre-computes a “Flattened Export Map” for each file, caching public symbols available via direct and transitive imports. - Resolution pass: Symbol references are resolved against the local file scope first (handling shadowing), then against the cached Export Map ($O(1)$ lookup).
3. Star Import Handling
The resolution logic explicitly handles “opaque” or star imports (import *). Search paths are flagged as “exhausted” if a star import is encountered, triggering a fallback search against the target file’s full public export set.
4. Separate Queries Per Language
Each language implements isolated tree-sitter queries for imports and references via import_query_fn() and reference_query_fn() in LanguageSpec.
References
- RFC 0008 - Graph Module and SCC Analysis
specs/graph-model.md
MetaCall Deploy Manifests
Research synthesis. 2026-06-24.
Status
Superseded. The original per-language manifest model (metacall.json + metacall.{tag}.json) was replaced by a pod-based model (metacall.pods.json) that partitions files by language via Union-Find and scopes dependencies per pod. The pod model is documented in DEPLOY.md. This RFC is retained as a design record of the original rationale and trade-offs.
1. What We Know
1.1 The Gap MetaCall Has
MetaCall’s runtime makes cross-language calls transparent at runtime but invisible at
deployment time. The canonical metacall.json manifest is deliberately minimal:
{ "language_id": "py", "path": ".", "scripts": ["__init__.py"] }
It declares what files to load but not how they relate to each other. Call topology is discovered at runtime by the core loader/inspector via reflection. This means:
- No pre-deployment validation of cross-language call paths.
- No topology visualization before deploy.
- No SCC detection across language boundaries.
- No static check that referenced scripts actually exist.
meta-ast already produces everything needed to fill this gap: cross-file dependency graphs, SCC analysis, language identification, symbol extraction with types, and deployability classification. Phase 5 wires this data into deploy manifest generation.
1.2 What the Codebase Already Has
| Component | Status | Location |
|---|---|---|
DeployabilityHint enum | Built | src/graph/scc.rs:38-46 |
DeployabilityStats | Built | src/output/graph.rs:31-36 |
SerializedScc | Built | src/output/graph.rs:81-90 |
GraphAnalysis (graph + scc) | Built | src/pipeline.rs:7-11 |
GraphOutput::from_graph() | Built | src/output/graph.rs:113 |
LangId (8 variants) | Built | src/language/mod.rs:67-80 |
CodeGraph.external_index | Built | src/graph/mod.rs:15-22 |
ExternalNode.language | Built | src/graph/node.rs:85-90 |
| Cross-language confidence (0.6) | Built | src/graph/resolver.rs:67 |
1.3 The Reference Fixture
tests/fixtures/mixed/auth_microservice/ demonstrates the target pattern:
auth.py(Python) callsmetacall_load_from_file('node', ['auth/auth.js'])to load Node.js functions, then callsmetacall('sign', text)andmetacall('verify', token)to invoke them.auth/auth.js(Node.js) exportssignandverifyusingjsonwebtoken.
The deploy tool must detect this metacall_load_from_file('node', [...]) call
and generate:
- A Python-side manifest listing the scripts.
- A Node.js-side manifest listing
auth/auth.js. - A root manifest composing both.
- A mesh annotation showing two cross-language deployment units.
1.4 MetaCall Language Tags vs meta-ast LangId
MetaCall uses short runtime tags. meta-ast uses descriptive LangId variants.
A mapping is required:
meta-ast LangId | MetaCall language_id |
|---|---|
Python | "py" |
JavaScript | "node" |
TypeScript | "ts" |
Tsx | "ts" |
C | "c" |
Cpp | "cpp" |
Rust | "rs" |
Go | "go" |
Note: Ruby (rb) and Java (java) are MetaCall-supported but not currently in meta-ast’s 8-language extraction engine. Deployment manifests for these will be generated if they are targets of metacall_load_from_* calls, but meta-ast will not extract symbols from their source files.
2. Architecture
2.1 Data Flow
Input Discovery
|
v
Parallel Parse + Extract (rayon)
|
v
Graph Assembly (GraphBuilder::from_extractions)
| \
v \
SCC Analysis (Tarjan) v
| [NEW] Preserve Vec<FileExtraction>
v |
Mesh Annotation Emitter v
from SCC Deployment Units [NEW] Call Site Scanner
| (per-file metacall_load_from_* detection)
v |
Deploy Manifest Generator <-----------------+
|
v
Root Manifest Assembler
|
v
[optional] --check validation against existing metacall.json
2.2 Module Layout
src/deploy/
mod.rs DeployConfig, DeployError, public API
tags.rs LangId -> MetaCall tag mapping, tag validation
scanner.rs Cross-Language Call Site detection (tree-sitter queries)
manifest.rs DeployManifest, RootManifest types + generation
mesh.rs MeshAnnotation type + SCC -> deployment unit converter
check.rs --check validation mode (diff against existing manifests)
2.3 Feature Gate
# Cargo.toml
[features]
default = []
embed-cytoscape = ["dep:handlebars"]
metacall-deploy = []
All src/deploy/ code is behind #[cfg(feature = "metacall-deploy")].
The CLI Deploy variant is gated. Zero compile-time cost when disabled.
3. Type System Design
3.1 Tag Mapping (src/deploy/tags.rs)
Maps meta-ast’s LangId to MetaCall runtime tags (py, node, ts, c, cpp, rs, go).
3.2 Cross-Language Call Site (src/deploy/scanner.rs)
The scanner uses tree-sitter queries per language pack to find calls to
metacall_load_from_file, metacall_load_from_memory, etc. It extracts:
- The first argument (language tag string literal or enum) ->
target_lang. - The second argument (array of script paths) ->
scripts.
Literals receive 1.0 confidence. Computed arguments (identifiers, calls, etc.) receive 0.4 confidence and are captured as their raw text.
3.2.1 Language Specific Patterns
- Python:
metacall_load_from_file(tag, scripts) - JS/TS/Tsx:
metacall_load_from_file(tag, scripts) - C/C++:
metacall_load_from_file(tag, paths, size)ormetacall_load_from_file_ex(...) - Rust:
metacall::load::from_files(Tag::NodeJS, ...) - Go:
metacall.LoadFromFile("node", ...)
3.3 Deploy Manifest (src/deploy/manifest.rs)
Defines DeployManifest and RootManifest structs compatible with MetaCall’s
format, plus an extension for multi-language projects.
3.4 Mesh Annotation (src/deploy/mesh.rs)
Mesh annotation emitted as metacall.mesh.json. Maps SCC-derived deployment
units to a mesh topology with cross-language boundaries and independence
classification.
4. Scanner Implementation Strategy
4.1 Tree-Sitter Queries
Implemented queries for all 8 supported languages using an “anchor and capture arguments node” strategy to ensure robust extraction of language tags and script path arrays.
4.2 Data Preservation
The current pipeline discards Vec<FileExtraction> after graph building.
The implementation uses a dedicated lightweight scanner pass that re-parses
files only when the deploy command is used, avoiding memory overhead on the
primary analysis path.
5. Output Formats
5.1 Per-Language Manifest (metacall.{tag}.json)
Groups discovered project files and detected call sites by target language.
5.2 Root Manifest (metacall.json)
Identifies the primary entry point language and composes all per-language packages.
5.3 Mesh Annotation (metacall.mesh.json)
Emits deployment unit topology derived from SCC analysis, identifying cyclic clusters and independent candidates across language boundaries.
6. Check Mode (--check)
Diffs generated manifests against any existing metacall.json in the project
tree and reports divergences (missing/extra scripts, language mismatches)
as structured diagnostics.
7. Testing Strategy
7.1 Example Validation Matrix
Verified against all fixtures in tests/fixtures/mixed/:
| Example | Status | Target Manifests | Mesh Expectation |
|---|---|---|---|
auth-function-mesh | PASS | metacall.py.json, metacall.node.json, metacall.json | 2 units, 1 edge |
auth-middleware | PASS | metacall.node.json, metacall.json | 1 unit |
string-manipulation | PASS | metacall.json | 1 unit (external) |
time-app-web | PASS | metacall.py.json, metacall.json | 1 unit |
7.2 Integration Tests
Implemented in tests/integration/deploy_test.rs, verifying manifest
correctness, root composition, and --check failure detection.
8. Risks and Tradeoffs
8.1 No Upstream MetaCall Extension
The enriched manifest format (with packages field and mesh annotation) is a
meta-ast extension, not an upstream MetaCall feature. MetaCall core will ignore
unknown fields. We target the meshfunction project’s deployment needs rather
than the core upstream loader.
8.2 Extraction Data Loss
Re-scanning files for call sites adds slight latency ‘1 ms’ but preserves a lean
memory model for the primary GraphAnalysis pipeline.
8.3 Unsupported Languages
Symbol extraction is limited to the 8 languages in meta-ast’s core engine.
Other languages (Ruby, Java) are handled as external nodes.
8.4 Dynamic Call Sites
Unresolvable arguments are assigned 0.4 confidence and annotated, aligning with NFR-2 (resilience).
RFC 0011: MetaCall Client API Support (metacall())
Status
Accepted and Implemented.
Implementation Notes
Implemented as designed with these notes:
metacall_handleis excluded because argument layout varies per port (tag first in C/Node, handle first in Rust).- Phase A and Phase B index all extracted symbols regardless of visibility flag.
- Rust
metacall_no_argandmetacall_untypedmap toClientCall;load::from_single_filemaps toLoadFromFile. - Client-call reference edges flow as distinct inter-pod reference edges without altering load confidence.
1. Problem
The deploy scanner previously detected only metacall_load_from_* calls. Client function calls were not tracked:
from metacall import metacall_load_from_file, metacall
metacall_load_from_file('node', ['auth/auth.js'])
def encrypt(text):
return metacall('sign', text) # Untracked client call
Without client call tracking, Function Mesh topology missed function-level cross-language dependencies and call-site attribution.
2. API Surface
The scanner detects client invocation APIs across all supported ports:
| API | Ports | Target |
|---|---|---|
metacall(name, ...) | py, node, C, C++, Rust, Go | function name string |
metacall_await(name, ...) | node, C, Go | function name string |
metacallfms(name, buffer) | node | function name string |
metacallv(name, args[]), metacallt(...) | C | function name string |
metacall_function(name) | C | function name string |
metacall::metacall, metacall_no_arg, metacall_untyped | Rust | function name string |
metacall.Call(...), metacall.Await(...) | Go | function name string |
3. Design
3.1 Model
CallSite includes ClientCall variant fields:
#![allow(unused)]
fn main() {
pub enum CallSiteVariant {
LoadFromFile,
LoadFromMemory,
LoadFromPackage,
LoadFromConfiguration,
ClientCall,
}
pub struct CallSite {
pub function_name: Option<String>,
pub is_async: bool,
// existing fields...
}
}
3.2 Two-Phase Function Resolution
client_call::resolve_client_calls resolves target functions:
- Phase A (Load-aware): Matches call names against public symbols in files explicitly loaded by the calling file. Matches score
1.0(unique) or0.8(ambiguous). - Phase B (Global fallback): Searches all project symbols if Phase A finds no match. Matches score
0.6(unique) or0.5(ambiguous). - Computed names: Dynamic arguments cap edge confidence at
0.4. Unresolved names produce a Warning diagnostic.
3.3 Graph Integration
Client-call edges are EdgeKind::Reference from calling file node to target symbol node. They participate in SCC analysis before pod partitioning, ensuring cross-language call cycles create proper cuts.
4. Impact
metacall.pods.json: Includesreferenceedges for cross-language client calls.metacall.mesh.json:cross_language_edgesattributes target deployment units and call-site files.
RFC 0012: Polyglot LSP Server over the .metast Index
Status
Approved.
1. Problem
MetaCall projects mix languages in one workspace. A function defined in Python is called from TypeScript through metacall(). No editor sees this: editors run one language server per file type, and each server knows nothing about the other languages. Cross-language navigation, hover, and completion are invisible.
Two past attempts failed:
intellisense: a VSCode extension, never a real language server. Per-request Python subprocesses for goto-definition (with an async race that missed on first use), placeholder types injected destructively into user.tsfiles, hardcoded type maps, and a committed cache with absolute Windows paths “first try”. Thevscode-languageclientdependency was dead weight.vscode-extension: deploy tooling and snippets only, no code intelligence “which doesn’t make sense”.
Meanwhile meta-ast already computes everything a code-intelligence backend needs: tree-sitter extraction for 9 languages, import resolution, a cross-language dependency graph with a confidence ladder, Tarjan SCC, call-site detection, and incremental re-analysis. But its output is a CLI artifact with no consumer loop except Function Mesh for now.
2. Proposal
The etags shape, upgraded:
meta-astemits a versioned, incrementally updatable index artifact (.metastv2).- A new LSP server consumes that index and serves requests for all supported languages from one process.
- Open-editor buffers feed the same extraction path, so unsaved edits stay consistent.
- Runtime metadata from
metacall inspectenriches hover when available; static features never depend on it.
The LSP does not re-parse anything. It serves queries from an immutable index snapshot and coordinates re-indexing when files change.
Home: the server lives in its own repository, metacall/lsp, not in this one. meta-ast stays a library dependency as discussed, Phase 0 patches below are the only changes required here.
3. Architecture
┌──────────────────────────────────────────────────────┐
│ Editor (VSCode / Neovim / emacs) │
│ thin client: launch server, forward buffers │
├─────────────────────────── LSP (stdio) ──────────────┤
│ meta-ast-lsp server │
│ ┌────────────────┐ crossbeam ┌───────────────┐ │
│ │ sync main loop │<-------------│ reindex worker│ │
│ │ answers from │ swap Arc │ runs engine │ │
│ │ Arc<IndexSnap> │------------->│ incremental │ │
│ └────────────────┘ └───────────────┘ │
│ │ embeds │
│ ┌───────────────────────▼──────────────────────┐ │
│ │ meta-ast engine (library, not CLI) │ │
│ │ WatchState + incremental_reanalyze │ │
│ │ BLAKE3 diff, per-file FileExtraction deltas │ │
│ └──────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────┤
│ Disk state │
│ .meta-ast/manifest.jsonl + per-file shards │
│ (cold start without full re-analysis) │
└──────────────────────────────────────────────────────┘
3.1 Engine layer
Embed meta-ast as a library inside the server process. Reuse directly:
incremental_reanalyze+WatchState: BLAKE3 fingerprint diff, re-extract only changed files, unchanged files keep their cachedArc<FileExtraction>.IdGenerator::with_start(max_cached_id + 1)seam: no ID collisions across ticks.GraphBuilder::from_extractions+add_edge_normalized: dedup and confidence rules stay canonical.
New seams required:
- In-memory ingestion: extract from
(uri, text, version)for open buffers instead of disk reads. This extendsextract_with_id_gen, it does not replace disk discovery. - Resolver invalidation:
PythonResolver,TsConfigResolver,GoModResolvercache filesystem state for the process lifetime. An LSP is long-lived, so these caches must invalidate on relevant config-file events. Until then, config changes require a server restart (documented limitation). - Debounce: reuse the notify-debouncer pattern from
src/watch/watcher.rs, driven by both OS events andtextDocument/didChangeflushes.
3.2 Index artifact (.metast v2)
Layout under .meta-ast/:
manifest.jsonl one line per file:
{ path, content_hash (BLAKE3 hex), size,
mtime, shard, schema_version }
shards/<n>.jsonl one block per file: symbols (full ranges),
imports, references, diagnostics, ast_node_count,
edges whose source or target belongs to the file
(endpoints as stable names)
header.json { schema_version, tool_version, created_at }
Rules:
- Hash bytes, never trust mtime alone. mtime and size are a fast negative check only (git checkout and rsync lie about mtime).
- Stable join key: language-scoped qualified name (SCIP-style descriptor, e.g.
python module . encrypt .). NumericSymbolIds are per-run and rayon-nondeterministic; they are regenerated in memory at load and never persisted. - Edge rows carry
(source_name, target_name, kind, confidence, flow_kind)so a merged load reproducesadd_edge_normalizedsemantics exactly. - Cold start loads
manifest.jsonl+ shards; warm path skips unchanged files entirely.
Optional exchange layer (Phase 3): meta-ast export scip writes a standard SCIP index so Sourcegraph-style consumers can read the same data “not needed for now”.
3.3 Server layer
Framework: lsp-server (rust-analyzer’s sync framework, actively released from the rust-analyzer monorepo).
Rationale for this specific shape:
- Indexed queries are hash-map lookups. A synchronous main loop answering from
Arc<IndexSnapshot>needs no async runtime. - Reindex coordination is plain threads: worker mutates nothing shared, then hands the new snapshot over the channel; main loop swaps the
Arc. - This mirrors rust-analyzer’s
main_loop.rstask-passing design without salsa. - Cancellation:
$/cancelRequestbecomes a token registry checked between handler phases. With sub-millisecond queries, cancellation pressure is minimal.
3.4 Client layer
Thin clients hosted under clients/ in metacall/lsp: VSCode and Zed first. Each client (~200 lines) activates on supported languages, spawns the server binary over stdio, forwards didOpen/didChange/didSave/didClose, and surfaces status. No intelligence lives in a client.
Editors without an extension story (emacs, vim, helix) work with zero client code: any LSP configuration pointing at the binary is enough. The TypeScript/Rust clients are deliberately simple onboarding tasks for new contributors arriving through Discord.
3.5 Distribution and packaging
The end user installs one extension and sees none of this machinery:
- CI publishes static server binaries per platform to GitHub releases of
metacall/lsp. - The client extension downloads the matching binary on activation and checks releases for updates automatically.
- If MetaCall itself is missing, the extension guides installation instead of failing silently. Static features never require MetaCall; only runtime enrichment (Phase 3) does.
- Optional later step: bundle the server into the existing MetaCall installer ‘not decided’, which already ships deploy and FaaS components. The maintainer confirmed this path stays open. Default stays GitHub releases because it decouples tooling releases from runtime releases.
4. Feature Phasing
Phase 0: meta-ast prep (prerequisite, small diffs)
serialize_symbol_nodeemitssource_rangeandfile_pathfor symbol nodes (currently dropped, which makes the graph export unusable for navigation).- Expose
extract_with_id_genover in-memory text. - Shard writer/reader module behind no new feature flag (it is pure output).
Phase 1: single-language correctness (not alive in this crate)
initialize, capabilities, workspace root handling (one root per server instance).- Incremental document sync + buffer overlay.
textDocument/documentSymbol(tree-sitter symbols already carry ranges).textDocument/hover: signature + docstring markdown.textDocument/definitionwithin one language via resolved references.- Publish diagnostics on re-extract.
Phase 2: the polyglot payoff
- Cross-language
definitionandreferencesrouted throughCodeGraphedges, includingmetacall()client-call edges with the RFC 0011 confidence ladder. workspace/symbol.textDocument/completion: bucketed symbol list with kind, signature, defining file; cross-language candidates ranked by edge confidence.- Debounced background reindex on file change with snapshot swap.
Phase 3: enrichment and ecosystem
- Hover merge with runtime metadata. Two sources, both optional enrichment:
- Local: parse
metacall inspectJSON (parameter and return annotations where the loader captured them, e.g. Python type hints). - Deployed: query a live FaaS deployment through the same
metacall/protocolAPI thatmetacall/deploy-mcp-serverwraps, so signatures reflect the running runtime. Absence of either changes nothing statically.
- Local: parse
- Semantic tokens (symbol kinds map cleanly).
- Stub generation (recycled concept from
deprecated/intellisense, done right): emit.pyi/.d.tsfrom the static model so native per-language servers also see cross-language signatures. Never write into user source files; write sibling stub directories configured as extra paths. Stubs are a one-way complement that feeds foreign signatures to native servers, they never carry navigation or diagnostics. The two-way channel is this LSP itself. meta-ast export scip.
5. Non-Goals
- Full type inference (RFC 0006 scope stands).
- Renaming across languages (write path; later decision if demand appears).
- Running or loading user code. Static analysis only; runtime inspection happens out-of-band via
metacall inspect. - Replacing native per-language servers. This server coexists; editors keep their TypeScript or Python LSP for deep single-language semantics and gain cross-language features from this one. We do not rebuild nine type systems: pyright and tsserver stay authoritative for deep inference.
6. Failure Modes We Will Not Repeat
From the old forensics:
- No destructive edits of user source. Stubs go to generated directories.
- No per-request subprocesses. Everything answers from the loaded snapshot.
- No placeholder or fabricated types. Absent metadata means absent hover detail.
- No committed caches with machine-specific paths.
- No VSCode-only logic. Intelligence lives in the server; clients stay thin.
7. Deliverables
meta-astPhase 0 patches in this repository: serializer ranges, memory-text extraction seam, shard IO. These land first sometacall/lspstarts against a capable library.metacall/lsprepository: server crate (meta-ast-lsp: engine host, sync server loop, index store) plus thin TypeScript clients underclients/vscodeandclients/zed.- Release engineering: per-platform binary builds published to GitHub releases; extension-side download and auto-update flow.
Verification for metacall/lsp follows the same repo standards:
cargo build --features watch --features metacall-deploy --features dataflow
cargo test --all-features
cargo clippy --all-targets --all-features -- -D warnings
cargo +1.94.0 fmt --check
Plus new integration tests: shard round-trip, snapshot swap under concurrent queries, cross-language definition fixtures under tests/fixtures/mixed/.