Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

ArtifactDescription
metacall.pods.jsonPod manifest (version 1.1) with deployment units, inter-pod edges, per-pair cut annotations, dependency lists, and AST metrics
metacall.mesh.jsonFunction Mesh topology with SCC deployment units and call-site attribution

With -f yaml the same documents are written as metacall.pods.yaml and metacall.mesh.yaml. MetaCall core reads JSON only, so the YAML form is a meta-ast artifact for people and for tooling that prefers YAML.

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

FlagDefaultDescription
-o, --out <dir>.Directory to write generated artifacts
-f, --format <json|yaml>jsonSerialization format
--checkoffCheck mode: cut fairness plus a diff of both manifests against the committed documents; exits non-zero on any issue
--max-pod-size <N>20Files per pod before rebalancing triggers

Pipeline

run_deploy()
  1. pipeline::analyze_graph()        - discovery, extraction, graph assembly, scope resolution,
                                        client call projection and SCC analysis in one pass
  2. scan_call_sites()                - MetaCall call-site detection over the extractions
  3. inject_load_edges()              - load edges into the graph, with path resolution
  4. orphaned_config_diagnostics()    - configuration files that no call site loads
  5. partition_graph()                - Union-Find over same-language Import and Reference edges
  6. detect_cuts()                    - cross-language SCC cuts plus oversized pods
  7. build_documents()                - file and pod metrics, dependency resolution,
                                        pod manifest and mesh annotation
  8. write_documents()                - atomic writes, or fairness plus manifest diff in --check mode

The run returns every diagnostic it collected to the caller, which applies the diagnostic policy and decides the exit status. An ecosystem answers dependency questions from one table of sources: lockfiles are preferred over manifests, a lockfile that does not carry the entry falls through to the manifest for Python, Node and Go, and only Python and Node answer from an immediate subdirectory.

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   one table of lockfile and manifest sources plus one reader per format
├── 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

VariantDetected functions
LoadFromFilemetacall_load_from_file, LoadFromFile (Go), bare use import (Rust), load::from_single_file (Rust)
LoadFromMemorymetacall_load_from_memory, LoadFromMemory
LoadFromPackagemetacall_load_from_package, LoadFromPackage
LoadFromConfigurationmetacall_load_from_configuration, LoadFromConfiguration
ClientCallmetacall, 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

Receiver resolution

A member call counts only when its receiver names the MetaCall runtime. bindings::resolve classifies the nearest declaration of the receiver name in the lexical scopes around the call, so all of these resolve:

  • import metacall as mc, import * as mc from "metacall", const mc = require("metacall")
  • import { metacall as m } from "metacall", const { metacall: m } = require("metacall")
  • import mc "metacall" (Go), use metacall as mc; (Rust), namespace mc = metacall; (C++)

Exported and parenthesized declarations bind like plain ones. A local declaration of the same name shadows the binding, so a call through a local mc is not a MetaCall site. The specifier must be metacall or a metacall/... subpath; metacall.js is a file, not the runtime. C and Ruby carry no module binding: only the bare API names count there.

Confidence scoring

CaseScore
String literal argument1.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 name0.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 an Import edge and a Reference edge connect the same pod pair, the fusion keeps the stronger confidence (max), never the product. A repeated (source, target, kind) triple merges the same way.

Language tag mapping

LanguageTag
Pythonpy
JavaScriptnode
TypeScript / TSXts
Cc
C++c
Rustrs
Gogo
Rubyrb

C and C++ share the c loader (libclang). Go has no MetaCall loader yet; deploy emits a warning and the tag stays for manifests.


Cut Detection

cut.rs implements two cut rules:

  1. 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.
  2. 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)ResolverLockfile (preferred)Manifest (fallback)
Pythonclassify_pythonuv.lock, poetry.lock, Pipfile.lockpyproject.toml, requirements.txt
JS / TS / TSXclassify_node_ecosystempackage-lock.json, yarn.lock, pnpm-lock.yamlpackage.json
Rustclassify_rustCargo.lockCargo.toml
Goclassify_gogo.sumgo.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.1",
  "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_annotations": []
    }
  ],
  "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:

  1. Every cut edge appears in manifest.edges[] with cut annotations.
  2. Cut edges have kind: "rpc_stub".
  3. Non-cut edges carry no cut annotation.
  4. The stub matches the cut direction; a reverse-direction stub alone fails.

Cut annotations land on the manifest edge between the cut’s own files, with one rpc_stub summary per pod pair. The stub confidence is the weakest finite link, or zero when no link is finite. Each cut_annotations list is in canonical order by source file, target file, cut reason, then confidence (total order), so permuted cut inputs serialize identically with no cuts dropped.

Check mode also diffs both generated documents against the committed metacall.pods and metacall.mesh files when they exist; a missing document skips the diff, so a fresh tree still checks clean.

run_deploy exits non-zero if fairness checks fail.


Edge-case Fixtures

Integration tests use fixtures in tests/fixtures/mixed/:

FixtureCoverage
auth_microserviceBaseline acyclic star graph (py loads go/node/ts).
auth_microservice_level2Cross-language SCC cycle, intra-language cycle collapse, dynamic and config loads.
auth_microservice_level3All 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:

  1. Add a static <LANG>_QUERY: LazyLock<Query> in scanner.rs.
  2. Add a dispatch arm to scan_file.
  3. Map tags in tags.rs.
  4. Add unit tests in scanner.rs.