complete
The walk finished.
Nothing that could add a caller went unseen. The value covers everything inside the declared scope.
Read it as: within this scope, this is the whole answer.
Deterministic code truth
ye-AST gives your agent what an IDE gives you: who calls a symbol, what depends on a change, where that dependence surfaces, and how complete the answer is. It derives every answer from code, git and declared contracts, and states what it could not see.
No LLM runs inside the engine. “Unknown” is an explicit answer, and an empty result never means “no impact”.
The fact contract
Every query returns a Fact. Its status decides whether an empty result means “none” or “not seen”.
complete
Nothing that could add a caller went unseen. The value covers everything inside the declared scope.
Read it as: within this scope, this is the whole answer.
partial
Each cause is a cut, and each cut names where it stopped: a bound, an unresolved name, a file with no extractor, or a path left out of the export.
Read it as: what lies past a cut is not known.
unknown
The reason names the cause: index-unavailable, source-unreadable, out-of-scope…
Never read it as “no impact”.
What makes a fact the same fact
The identity is the only invalidation key. Change the scope, the content, the config or an engine version and it is a different fact.
src/core/domain/shared/fact.ts:73-90, 193-194 · RFC-0002 rule 5
Reading a partial fact
Every partial fact lists its cuts by these names (src/core/domain/shared/fact.ts:114-128). The MCP server's instructions tell an agent that a partial answer keeps its known findings and lists its cuts, and that it should raise a bound only when a cut names it (src/mcp/server.ts:50-69). The table shows what to do about each one.
| Cut | What it means | What to do |
|---|---|---|
depth | The walk stopped at the depth bound. | Raise budget.depth, or ask callers about the symbol at the cut. |
hub | A reached symbol has more call-like callers than the hubFanIn budget, so the walk did not expand it. The symbol is still in the answer; what stops is the climb through it. | Raise hubFanIn, or ask callers about that symbol alone. |
caller-budget | expansions ran out, or an export budget stopped the snapshot. | Raise the bound, or narrow the request with scope. |
unresolved-link | A name the index could not resolve may point at a walked symbol. | Open each place and decide; after a deletion, each place still naming it breaks. |
no-extractor | A file ye-AST cannot read: a template, a style sheet, an uncovered language. | Read it yourself when it bears on the task. |
not-exported | A path the snapshot left out (symlink, submodule, unsafe path). | Read it yourself; nothing under it was read. |
deadline | Time ran out before the answer was whole. | Ask again; narrow the scope if it repeats. |
section | One section of a composite answer could not be produced. | The rest stands; asking again may complete it. |
node-cap | A walk stopped at a node cap. | Narrow the scope or ask about fewer symbols. |
Surfaces
A driving adapter turns an outside request into a call and shapes the answer. It never calls an adapter or a use case directly; composition injects the query families.
| Surface | What it exposes | Where |
|---|---|---|
ye-ast map | The entrypoints of a revision; --live for a trusted working tree. | src/cli/main.ts:296-299 |
ye-ast impact | The change from the merge base to head, with bound and scope options. | src/cli/main.ts:300-303 |
ye-ast callers | What depends on symbols or line ranges. | src/cli/main.ts:304-307 |
ye-ast importers | Importers, users and unresolved namers per file. | src/cli/main.ts:308-311 |
ye-ast outline | Declaration spans and imports of the listed files; files with no node in the index are listed as unheld. | src/cli/main.ts:312-315 |
ye-ast workspace | The outward names a repository uses, the candidate repositories for each and the names no repository serves; --offline refreshes no remote. | src/cli/main.ts:316-319 |
ye-ast decisions | The configuration gaps the code cannot fill, each with evidence-backed options; it writes nothing. | src/cli/main.ts:320-323 |
ye-ast init | Writes the trust record, the local exclude and the fixed layer; prints the MCP registration. | src/cli/main.ts:104 · src/core/application/install/install.ts:26 |
ye-ast mcp | MCP server over stdio with read-only tools; it serves the declared repositories, and each call picks one with repo. | src/composition/mcp.ts:9-23 |
ye-ast serve | The query protocol over stdio; stdout carries only protocol lines. | src/protocol/query-server.ts |
Read-only by type. The MCP server receives only read query families: EntrypointQueries, ImpactQueries, OutlineQueries, WorkspaceQueries, WorkspaceImpactQueries, DecisionQueries and ContractConsequencesQueries. No write method exists, and all ten tools are annotated read-only.
The server also includes instructions: when to call each tool and how to read an answer. Complete is relative to the declared scope, partial keeps its known findings and lists its cuts, unknown never means no impact, and a bound is raised only when a cut names it.
One server serves several declared repositories: ye-ast mcp [<launch-root>] [--repo <id> <root>]... declares them, and each call chooses one with repo (the launch repository by default). Registering a repository grants it no trust.
src/mcp/query-runner.ts:27-37 · src/mcp/server.ts:50-69 · src/composition/mcp.ts:9-23
The other tools and controls
ye_ast_outline returns the kind, name and line span of each declaration and every import with where it leads and the names it binds, each with the declaration and line it reaches when proven, never source text. A focus on one declaration adds what its own body uses outside itself: a declaration with its file and line, a package or a global, an unproved use being a cut. Requested files the index holds no node of come back as unheld, which is not “nothing”.ye_ast_trace returns what a declaration executes, in source order, each declaration expanded once, with a reading plan of the line spans to open. Branches are not evaluated, and a call no rule settles is an unresolved-link cut with its candidates, never a guess.ye_ast_workspace returns the outward names a repository uses (HTTP, messaging, dependencies), the candidate repositories for each and the names no repository serves. Local clones you supply are used as given, never downloaded.ye_ast_decisions returns the gaps the code cannot fill (membership, repository, library, gateway, revision, remote, trust), each with evidence-backed options. It writes nothing.ye_ast_contract_consequences returns what a request or response contract change breaks, from written hypotheses or from a diff of the contract: each consumer use or request producer with its verdict and the chain that proves it, and every place the flow is lost as unknown. An answer without findings never means the change is safe.mode changes presentation only, never coverage: summary gives findings, details evidence and locations, full the complete result JSON with navigation text.select and page go together, and page.limit is required. expect asserts a result identity; replay reruns the query and fails if the result changed, because ye-AST keeps no earlier capture.src/mcp/server.ts:50-69 · src/mcp/query-schema.ts:158-205 · src/mcp/presentation.ts:5-8 · src/core/domain/outline/outline-fact.ts · src/core/domain/decisions/decision.ts:4
MCP only · ye_ast_compose
Send 1 to 16 typed queries of the eight families (entrypoints, impact, callers, importers, outline, trace, workspace, decisions) and get one merged answer. Batch related queries that share entities; when a later query depends on a coordinate, get that coordinate first.
Try a combination
Real answers captured on a two-repository fixture (shop and web). Composing adds overhead: a lone query, queries that share nothing, or full mode can cost more than separate calls. In return you get one merged answer with its connections and coverage.
composition-source-fault.handler-declaration, reached-handler, linked-entrypoint, change-dependent, shared-file-import, used-import and shared-support link findings only between reads with the same schema, producer, trust, engines, workspace and snapshot. shared-file-import and shared-support only repeat an exact observation, so they also accept another version of the same producer. Otherwise a context-mismatch or snapshot-difference issue is reported. shares-evidence means the same location, not causality.summary and details modes return text plus a receipt: a verification identity (mcp-c1) and the requests to run again. Send them back with expect and the same set must still give the same result.src/mcp/composition-input.ts:13-19 · src/mcp/compose-request.ts · src/core/application/composition/compose-evidence.ts:207-237, 600-682 · src/mcp/render-composition.ts:93
Trust & snapshots
A repository never raises its own trust. Trust describes the repository's content, never the machine, and you declare it outside the repository: a host does it through the library or the protocol, a user through ye-ast init or a flag. Without a declaration, the CLI and the MCP server run untrusted.
Reads only a snapshot exported from git at a sha, never the repository itself.
ls-tree lists the tree; only in-scope files are selected.cat-file --batch streams each blob into a scratch directory..gitignore, engine config or pre-built index is honored.Adds the working tree and the remote refresh, always into ye-AST's own store.
--live replaces head/revision with the working tree.ls-files -z -s --cached and untracked ones with ls-files -z --others --exclude-standard.sync({ paths }) keep the graph of those bytes.--live is refused under untrusted with live-requires-trusted.The live rule: a live fact describes bytes ye-AST copied and hashed, and the graph it reads is the graph of those same bytes. The engine never walks the repository.
docs/architecture/rfc/0010-impact-queries.md “Live head” · src/core/application/live/live-tree.ts
Invariants
src/mcp/query-runner.ts:27-37).unknown/partial, never an empty result.Architecture
Pick a flow and step through it: each step lights the modules and edges it crosses and explains why. Select a module to see what it may import and what it never reaches. Hold Ctrl (⌘ on a Mac) while scrolling to zoom the graph.
Flow
The walk
These rules decide which edges a traversal follows and what the answer can see. The simulator applies them to a toy repository, and two real runs show the result.
hub cut there. The symbol stays in the answer; the budget only stops the walk from climbing through a shared utility that would otherwise pull in the whole repository. It does not count the calls the symbol makes.A route names a class, not a method. When the walk reaches a method, it also reaches the type that declares it, at the same depth and with confidence 1 (walk.ts:82-87). That type then expands only through whole-type edges: extends, implements, and Angular uses-component / uses-directive / uses-pipe. It does not expand into whoever injects or imports it — otherwise a single reached member would pull in everything.
src/core/domain/impact/edge-policy.ts:33-39 · src/core/application/impact/inbound-edges.ts:92-102
codegraph's interface-impl bridges stop silently after 40 per class-supertype pair, so ye-AST derives its own: a method and the same-named method of a type above it, for every reached node; downward only when the reached declaration is the changed contract, because a changed declaration changes what its implementations must meet. A path that went up never comes back down to a sibling.
src/core/application/impact/inbound-edges.ts:123-169 · src/core/domain/impact/edge-policy.ts:50-55
this.field.member problem field originthis.auth.login() is an unresolved reference, not a call edge. If the class declares auth with a fixed origin, ye-AST settles the use: a field holding the member's type makes the code a caller; a field holding an unrelated type settles it as no use; a field holding a subtype leaves it unsettled. That is why the walk can follow real dispatch without turning every same-named method into a caller.
src/core/application/impact/inbound-edges.ts:265-302 · src/core/domain/impact/edge-policy.ts:40-45
A path is only as sure as its least sure edge, and the walk reports the best such path from any seed. A heuristic edge without a score counts 0.3. In the real run: 1 at the seed, 0.9 at login, 0.85 at onSubmit. Fan-in is separate: it counts call-like callers, whether or not the walk followed them, so a renderer can rank or collapse hubs without the fact cutting them.
src/core/application/impact/walk.ts:172-188 · src/core/domain/impact/edge-policy.ts:77-101
| Edge into a reached node | Followed | Why |
|---|---|---|
calls, instantiates from a function, method, class, component, constant, variable or field initializer | yes | Behavior depends on the target. |
An unresolved this.<member> or this.<member>.<rest> inside the class that declares it | yes | The class's own code uses the member. |
references into a function, method, value or class | yes | The source uses it. |
references into an interface or type alias (a DTO) | yes | A type change breaks its users; qayaba does not follow these. |
extends, implements into a class or interface | yes | A subtype depends on its supertype. |
| Interface method → implementation method of the same name, derived by ye-AST | yes, both ways | Dispatch at run time; codegraph's interface-impl bridges stop silently after 40 per pair. |
A reached method → its type, at the same depth; from that type only extends/implements and uses-* | yes | Routes and components name the class, not the method; ADR-0017 keeps the class and stops it expanding into injectors. |
imports into a file node | no | A file node leads nowhere. |
imports into a symbol | yes when used | Only when the importing file's top-level code uses it. |
| An injectable class with no caller → its importers' top-level code | yes | Injection without a static call. |
injects, uses-component, uses-directive, uses-pipe | yes, confidence 1 | Read from the Angular sources, not guessed. |
navigates | no | A router link is navigation, not dependence. |
codegraph's angular-template and angular-event edges | no | ADR-0014; ye-AST's own A2/A4 relations bring their own. |
| Any edge from a test file | no | Tests are not callers; the declared scope says so. |
| Containment | no | Structure, not dependence. |
Every query family stamps the same producer version (ADR-0024). src/core/domain/impact/edge-policy.ts:14-58 · docs/architecture/rfc/0010-impact-queries.md:88-109
Walk simulator
The engine does not run in this page. This is a faithful model of the walk on a small graph, using the same edge policy, owner expansion, confidence and cuts. Each rule it applies is cited below.
hub fan-in is a budget on fan-in: the number of call-like callers a symbol may have before the walk stops expanding it and emits a hub cut. Leave it empty for no bound. depth counts hops from the seed; expansions counts expanded symbols.
| depth | symbol | file | confidence | fan-in | via |
|---|
The toy graph is illustrative; the rules are not. Only reached symbols are drawn: the strip below lists the rest, and the policy trace names every edge the walk refused and why. The walk expands each symbol once, enters a reached method's declaring type at the same depth, and follows only the rows of the edge policy: a test file is never a caller, navigation is not dependence, and a file import leads nowhere.
Reference run
Both fixtures were created outside the worktree, read as untrusted, and queried with this build. The values below are copied from the JSON output.
plain TypeScript
ye-ast impact <repo> --base HEAD~1
The seed is validateToken. The walk climbs to AuthService.login and AuthController.loginHandler; the classes join at the same depth as their methods. This fixture has no entrypoints, and that is not a failure.
snapshot 4e00fd9b293a · merge base 8dba227feee2 · trust untrusted
Angular routes
ye-ast impact <repo> --base HEAD~1
The same change, now with a route table. The walk reaches LoginComponent through onSubmit, and the join surfaces /login. The cut comes from the snapshot, not the change: a .rb file has no extractor.
snapshot 2dff4ecee21a · merge base d2a9f2b9368b · cut no-extractor at src/legacy/report.rb
src/auth/token.ts
export function validateToken(token: string): boolean {
return token.length > 40; // changed: was > 20
}
src/auth/auth.service.ts
export class AuthService {
login(token: string): boolean {
return validateToken(token);
}
}
src/ui/login.component.ts
@Component({ selector: "app-login", … })
export class LoginComponent {
constructor(private readonly auth: AuthService) {}
onSubmit(token: string): boolean {
return this.auth.login(token);
}
}
src/app.routes.ts
export const routes = [
{ path: "login", component: LoginComponent },
{ path: "dashboard", component: DashboardComponent,
canActivate: [AuthGuard] },
];
This is the Angular fixture behind the partial run; the plain run differs only in the caller, an AuthController instead of the component. The change touches one line of token.ts. The walk climbs three hops, and the join turns the component into /login. The test file that also calls login is in the repository but is never a caller, by policy.
Limits
What this page leaves out, and the commands that reproduce its runs. Glossary terms are highlighted in the text: select one to read its definition.
Limits of this page
Reproduce the evidence
# the two fixtures are ordinary git repositories node --import tsx src/composition/main.ts map <fixture> node --import tsx src/composition/main.ts impact <fixture> --base HEAD~1 node --import tsx src/composition/main.ts callers <fixture> src/auth/token.ts:validateToken node --import tsx src/composition/main.ts importers <fixture> src/auth/token.ts # the architecture gate npm run check