artifact-contracts-analyzer
v0.3.4
Published
Artifact traceability analyzer — trace elements, resolve links, and validate coverage across artifact boundaries
Maintainers
Readme
artifact-contracts-analyzer
Analyze project source code through the lens of agent contracts.
artifact-contracts-analyzer bridges the gap between design-time contract definitions (agent-contracts) and runtime execution (agent-contracts-runtime) by analyzing actual project source code against the contract graph.
It answers the questions that neither the DSL compiler nor the runtime can answer alone:
- What code is actually affected when an artifact changes?
- What context should an agent receive for a given task?
- How do source-level dependencies map to contract-level relationships?
Why artifact-contracts-analyzer?
agent-contracts knows the contract graph — which agents own which artifacts, which tools produce or consume them, and what routes a change should follow. But it operates on DSL definitions only. It cannot look inside project source files.
agent-contracts-runtime knows how to execute agent workflows — task scheduling, SDK adaptation, guardrail enforcement. But it treats context as an opaque input provided by the caller.
The missing layer is source-aware analysis: reading actual project files, parsing their structure, and connecting code-level dependencies to contract-level relationships.
agent-contracts artifact-contracts-analyzer agent-contracts-runtime
(contract graph) (source analysis) (execution)
"api-schema's producer "src/generated/client.ts "Run implementer with
is generate-api-client imports from openapi.yaml. this context pack and
tool, and its source src/handlers/*.ts imports validate the result."
is openapi-spec." from client.ts."
DSL YAML only DSL + project source files contracts + contextWithout this layer, agents either receive too much context (entire repository) or too little (manually curated file lists). Neither scales.
What it does
1. AST Graph (low-level)
Parse project source files into a language-aware code structure graph.
Input:
Project source tree + artifact path_patterns from DSL
Output:
Code structure graph
├── file nodes (kind: "file")
├── symbol nodes (kind: "symbol", with symbolId = "filePath#name")
├── imports / exports / re_exports edges
├── calls edges (caller symbol → callee symbol)
├── type_reference edges
└── contains edges (file → symbol it defines)The AST Graph captures intra-file structure and inter-file references at the symbol level. Beyond file nodes, it materializes a node per declared symbol (functions, classes, types, etc.), each identified by a stable symbolId of the form filePath#name and carrying its 1-based source line range. contains edges link a file to the symbols it defines, and calls edges connect a calling symbol to the symbol it invokes. It is a raw, deterministic representation of what the code says — no interpretation, no inference.
This is the lowest-level graph in the system. It does not know about artifacts, packages, or contract boundaries. Higher-level graphs consume it.
2. Dependency Graph (high-level)
Build a project-wide dependency graph by integrating the AST Graph with package manifests and contract artifact boundaries.
Input:
AST Graph (code-level edges)
+ package manifests (package.json)
+ artifact path_patterns (from DSL)
Output:
Unified Dependency Graph
├── symbol nodes + symbol-to-symbol edges (calls, type_reference, contains)
├── file-to-file edges (from AST Graph)
├── external package dependencies (from manifests)
└── artifact boundary crossings (file → artifact mapping)The Dependency Graph preserves the AST Graph's symbol nodes and symbol-to-symbol edges (such as calls and type_reference) alongside the file and package layers, so a query can drill from artifacts down to individual functions. It is the integration layer that connects code structure to contract structure. It answers: "when file A — or the symbol file#fn — changes, which other files, symbols, and artifacts are downstream?"
AST Graph Dependency Graph
(code structure, symbol-level) (project structure, artifact-aware)
│ │
└──────── consumed by ────────────→│
│← package manifests
│← artifact path_patterns3. Impact Analysis
Given a changed artifact or set of changed files, compute the downstream impact — what is affected by the change.
Input:
Navigation Index (from agent-contracts)
+ Dependency Graph
+ change seeds: changed files, changed symbols, an artifact ID,
or a git diff ref
Output:
Impact Report
├── affected entries (AffectedEntry: path, plus symbolId and line/endLine
│ when the affected node is a symbol)
├── directly affected files (immediate dependents)
├── transitively affected files (downstream closure)
├── affected artifacts (mapped back to DSL)
├── affected validations (from artifact required_validations)
├── affected agents and workflows
└── confidence annotations per edgeChange seeds can be specified at multiple granularities:
- Files (
--files) — whole-file change seeds, as before. - Symbols (
--symbols) — symbol-level seeds of the formsrc/server.ts#handleRequest, so impact starts from a single function rather than the whole file. - Git diff (
--diff <ref>) — derives changed symbols automatically by diffing against a ref (e.g.HEAD~1,main), mapping each changed line range back to the symbol whose source range contains it.
Each result is an AffectedEntry carrying the file path; when the affected node is a symbol it also carries its symbolId and line/endLine range, so consumers know exactly which declarations are in scope.
Impact Analysis traces reverse dependencies from the change point outward:
changed file/artifact
→ reverse references (who imports/calls this?)
→ affected callers (downstream transitive closure)
→ affected artifacts (mapped via path_patterns)
→ affected validations (from artifact required_validations)
→ affected agents/workflows (from Navigation Index routes)The default direction is downstream impact — what breaks or needs updating when this changes. Upstream analysis (what does this depend on?) is available as a separate query mode but is not the primary use case.
The Navigation Index provides contract-level relationships (tool operations, artifact relations, action routes). The Dependency Graph provides code-level relationships. Impact Analysis overlays both to determine the full scope of a change.
4. Context Pack Generator
Produce a minimal, relevant context pack for an agent task — the right files, the right sections, the right metadata.
Input:
Impact Report (or task + artifact scope)
+ Dependency Graph
+ project source files
Output:
Context Pack
├── relevant source files (ranked by relevance)
├── relevant artifact definitions
├── dependency context (what depends on what)
└── size budget enforcementInstead of passing an entire repository or relying on manual file lists, the Context Pack Generator uses graph analysis to select the minimal set of files an agent needs to complete a task effectively.
Two extraction granularities are supported (--granularity):
symbol(default) — only the line ranges of the affected symbols are extracted.file— each selected file is included in full.extractSymbolSectionsturns the impact report's affected symbols into one section per symbol (spanning itsline…endLine), sorted and de-overlapped, so the pack carries the relevant functions and types rather than whole files. This packs far more relevant context into the same token budget.
5. Provenance and Evidence Model
Every edge in the graph carries provenance metadata: an edge classification and an array of evidence recording why the edge exists.
The graph build produces deterministic edges from static AST analysis, manifests, and path pattern matching. Each edge carries structured evidence:
edge:
from: src/routes/api.ts
to: src/services/user-service.ts
type: deterministic
source: ast_import
confidence: 1.0
evidence:
- type: static_import
detail: "import { UserService } from '../services/user-service'"
line: 3computeRelevanceScore assigns each edge a relevance score from the kinds of evidence backing it (e.g. static_import outweighs heuristic links), with a bonus when several distinct evidence kinds corroborate the same edge.
artifact-analyzer semantic detects gaps in the deterministic graph and proposes candidate edges via LLM inference. Candidate edges are persisted to the store and automatically merged into the graph on subsequent impact and context-pack runs. Use --edge-types deterministic to exclude them.
6. Call Graph
Walk the calls edges from an entry symbol to build a bounded call tree. The traversal runs in either direction:
- Forward (default) — follow
callsedges caller → callee: "what does this function call, transitively?" Useful for understanding a function's full call chain before changing it. - Reverse — follow
callsedges callee → caller: "who calls this function, up to the entry points?" Callers surface at the leaves. Useful for verifying test coverage (which tests reach this symbol?) and assessing blast radius.
Cycles (recursion, mutual recursion) are detected per root-to-node path and truncated, so the tree is always finite, and traversal is bounded by a maximum depth.
# Forward: what does handleGet call, up to 3 levels deep?
artifact-analyzer callgraph --entry 'src/api.ts#handleGet' --depth 3
# Reverse: who calls parse() — including which tests reach it?
artifact-analyzer callgraph --entry 'src/utils.ts#parse' --direction reverseThe global --format selects the rendering: an indented tree (text), a Graphviz digraph (dot), or the raw tree structure (json).
Architecture
Inputs and outputs
┌─────────────────────────┐ ┌──────────────────────────┐
│ agent-contracts │ │ Project Source Tree │
│ │ │ │
│ resolve() ───────────────┼──┐ │ src/, lib/, tests/, ... │
│ buildNavigationIndex() ──┼──┤ │ package.json │
│ │ │ │ config files │
└─────────────────────────┘ │ └──────────┬───────────────┘
│ │
▼ ▼
┌──────────────────────────────┐
│ artifact-contracts-analyzer │
│ │
│ buildAstGraph() (low) │
│ ↓ │
│ buildDependencyGraph() (high) │
│ ↓ │
│ analyzeImpact() │
│ ↓ │
│ generateContextPack() │
└──────────────┬───────────────┘
│
┌──────────────┼───────────────┐
▼ ▼ ▼
Impact Report Context Pack Dependency Graph
│ │
▼ ▼
┌─────────────────────────────┐
│ agent-contracts-runtime │
│ │
│ runWorkflow(invocation, { │
│ contextPack: pack │
│ }) │
└─────────────────────────────┘Internal data flow
Source files ──→ AST Graph (symbol-level, deterministic)
│
├── + package manifests
├── + artifact path_patterns
▼
Dependency Graph (project-level, artifact-aware)
│
├── + Navigation Index (contract-level)
├── + changed files
▼
Impact Report (downstream closure + evidence)
│
├── + source file contents
├── + token budget
▼
Context Pack (optimized agent input)Package dependencies
artifact-contracts-analyzer
dependencies:
agent-contracts — resolve(), buildNavigationIndex(), DSL types
peerDependencies:
better-sqlite3 — optional: persistent index store (in-memory fallback)artifact-contracts-analyzer depends on agent-contracts for DSL resolution and the Navigation Index API. It does not depend on agent-contracts-runtime — runtime integration lives in a separate package (artifact-contracts-analyzer-plugin), so the analyzer can be used standalone for impact analysis and dependency visualization.
Boundary with agent-contracts
| Concern | agent-contracts | artifact-contracts-analyzer |
|---------|----------------|--------------------------|
| Input | DSL YAML (+ cli-contract YAML) | DSL + project source files |
| Contract Graph | Builds from DSL | Consumes as input |
| Navigation Index | Builds from DSL | Consumes as input |
| AST analysis | None | Core capability |
| File system scanning | artifact-coverage (pattern matching only) | Deep source analysis |
| Impact analysis | None | Core capability |
| Context generation | Template rendering (Handlebars) | Graph-based context selection |
Boundary with agent-contracts-runtime
| Concern | agent-contracts-runtime | artifact-contracts-analyzer |
|---------|------------------------|--------------------------|
| Agent execution | Core capability | None |
| SDK adaptation | Core capability | None |
| Workflow DAG scheduling | Core capability | None |
| Context pack consumption | Receives and passes to agents | Produces |
| Plugin system | Provides AgentPlugin interface | Bridged by separate artifact-contracts-analyzer-plugin package |
| Guardrail enforcement | Core capability | None |
Runtime plugin integration
Runtime integration is provided by a separate package, artifact-contracts-analyzer-plugin, which depends on both the analyzer and agent-contracts-runtime. The analyzer core stays free of runtime knowledge:
import { createAnalyzerPlugin } from 'artifact-contracts-analyzer-plugin';
import { pluginRegistry } from 'agent-contracts-runtime';
const plugin = createAnalyzerPlugin({
projectRoot: '.',
index: prebuiltIndex,
});
pluginRegistry.register(plugin);The plugin implements the AgentPlugin.contextEnhancer interface — no changes to agent-contracts-runtime are required.
Deterministic-first principle
The graph build produces deterministic edges from code structure:
| Priority | Method | Confidence | Example |
|----------|--------|------------|---------|
| 1 | Static AST analysis | 1.0 | import { foo } from './bar' |
| 2 | Manifest resolution | 1.0 | dependencies in package.json |
| 3 | Path pattern matching | 1.0 | path_patterns: ["src/generated/**"] |
artifact-analyzer semantic can propose candidate edges via LLM inference for gap detection. Candidate edges are persisted and merged into subsequent pipeline runs. Use --edge-types deterministic to restrict to confirmed relationships only.
Capabilities
CLI
# Build and display the AST dependency graph
artifact-analyzer graph --project-root .
# Analyze impact of changes to a specific artifact
artifact-analyzer impact --artifact api-schema
# Analyze impact of specific file changes
artifact-analyzer impact --files src/api/schema.ts src/api/types.ts
# Analyze impact starting from a single symbol
artifact-analyzer impact --symbols 'src/server.ts#handleRequest'
# Derive changed symbols from a git diff and analyze their impact
artifact-analyzer impact --diff HEAD~1
# Build a forward call graph rooted at a symbol
artifact-analyzer callgraph --entry 'src/api.ts#handleGet' --depth 3
# Build a reverse call graph (who calls this symbol?)
artifact-analyzer callgraph --entry 'src/utils.ts#parse' --direction reverse
# Generate a context pack (symbol-scoped by default)
artifact-analyzer context-pack --files src/api.ts --task implement-feature --budget 50000
# Generate a file-granularity context pack
artifact-analyzer context-pack --files src/api.ts --granularity file
# List dependencies of a file
artifact-analyzer dependencies --file src/api.ts --format jsonLibrary API
import {
buildAstGraph,
buildDependencyGraph,
createDefaultRegistry,
analyzeImpact,
buildCallGraph,
generateContextPack,
} from 'artifact-contracts-analyzer';
// Artifact patterns: artifactId → glob patterns (from DSL path_patterns).
const artifactPatterns = new Map([
['api-schema', ['src/generated/**']],
['handlers', ['src/handlers/**']],
]);
const registry = createDefaultRegistry();
const { graph: ast, fileContents } = await buildAstGraph({
projectRoot: '.',
artifactPatterns,
registry,
});
const graph = await buildDependencyGraph({
ast,
projectRoot: '.',
artifactPatterns,
manifests: ['package.json'],
});
// Symbol-level impact: seeds may be files OR symbolIds ("filePath#name").
const impact = await analyzeImpact({
graph,
changed: ['src/server.ts#handleRequest'],
direction: 'downstream',
});
for (const entry of impact.directlyAffected) {
if (entry.symbolId) {
console.log(`${entry.symbolId} (lines ${entry.line}-${entry.endLine})`);
}
}
// Call graph: who calls this symbol, up to the entry points?
const callGraph = buildCallGraph({
graph,
entry: 'src/utils.ts#parse',
direction: 'reverse',
maxDepth: 3,
});
// Context pack: symbol-scoped by default.
const contextPack = await generateContextPack({
graph,
changed: ['src/server.ts#handleRequest'],
taskId: 'implement-feature',
budgetTokens: 50_000,
projectRoot: '.',
fileContents,
});Who this is for
artifact-contracts-analyzer is a fit for teams that:
- use
agent-contractsto define multi-agent workflows - want agents to receive optimized, relevant context instead of entire repositories
- need impact analysis to understand what a change affects before delegating to agents
- operate at scale where manual context curation is not sustainable
- want to integrate source analysis into existing
agent-contracts-runtimeworkflows
Who this is not for
- Teams not using
agent-contracts— the analyzer requires a contract graph as input - Single-file or trivial projects — the overhead of graph analysis exceeds the benefit
- Teams that need only runtime orchestration — use
agent-contracts-runtimedirectly
Relationship to the ecosystem
┌────────────────────────────────────────────────────────────────┐
│ agent-contracts ecosystem │
│ │
│ ┌──────────────┐ ┌─────────────────┐ ┌──────────────────┐ │
│ │ │ │ │ │ │ │
│ │ agent- │ │ agent- │ │ agent- │ │
│ │ contracts │──│ contracts- │──│ contracts- │ │
│ │ │ │ analyzer │ │ runtime │ │
│ │ (design) │ │ (analysis) │ │ (execution) │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └─────────────────┘ └──────────────────┘ │
│ │
│ DSL → Contract Contract Graph → Contracts + Context → │
│ Graph Source Analysis → SDK Execution → │
│ Context Pack Validated Results │
└────────────────────────────────────────────────────────────────┘The three packages form a pipeline:
agent-contracts— define what the system looks like (contracts)artifact-contracts-analyzer— understand what the code looks like (analysis)agent-contracts-runtime— execute agent tasks with optimal context (execution)
Each package is independently useful, but the combination enables contract-aware, source-optimized agent execution.
AST cache
The analyzer caches per-file AST parse results keyed by content hash (SHA-256). On subsequent invocations, only files whose content has changed are re-parsed. The cache is stored locally (SQLite with in-memory fallback) and can be inspected or cleared via artifact-analyzer cache status / cache clear.
Evidence model
Every edge in the graph carries structured evidence explaining why the relationship exists:
edge:
from: src/routes/api.ts
to: src/services/user-service.ts
type: deterministic
source: ast_import
confidence: 1.0
evidence:
- type: static_import
detail: "import { UserService } from '../services/user-service'"
line: 3Evidence types include static_import, manifest_dependency, path_pattern_match, and others. This makes every edge auditable (reviewers can inspect why two files are linked) and debuggable (unexpected impact results can be traced to their evidence).
Tech stack
| Category | Choice |
|----------|--------|
| Language | TypeScript (ESM, strict mode) |
| AST parsing | TypeScript Compiler API (built-in) |
| Cross-file resolution | TypeScript Compiler API via ProjectAnalyzer (resolves calls/type_reference targets to cross-file symbolIds) |
| Graph | In-memory adjacency graph with serialization |
| Schema | Zod |
| CLI | commander |
| Testing | Vitest |
| Build | tsup |
Status
This project is implemented and usable for TypeScript/JavaScript projects. The full pipeline — AST graph, dependency graph, impact analysis, call graph, and context pack generation — is available through both the artifact-analyzer CLI and the library API, including symbol-level analysis (symbol nodes, calls edges, symbol-level impact seeds, git-diff-derived seeds, and symbol-scoped context packs). A runtime plugin bridge (artifact-contracts-analyzer-plugin) is provided as a separate package. As a pre-1.0 release, the API surface may still evolve.
License
MIT
