Skip to content

aft_callgraph and aft_outline can't see interface methods or object-property functions (TypeScript) #329

Closed

Description

@johnpwilkinson

What happened?

I asked aft_callgraph (op: "callers") who calls parse in packages/zod/src/v4/classic/schemas.ts in the Zod repo. It said the symbol doesn't exist. It does, twice: once as a method signature inside export interface ZodType (line 71), and once as an object property parse: function _parse(...) inside the ZodType constructor body (line 297). Neither is findable.

The same call works fine on parse in packages/zod/src/v4/classic/parse.ts, which is a plain export const. That one correctly reports a single caller at schemas.ts:298. So the call graph is built and healthy; it just doesn't know about methods declared on interfaces or functions assigned as object properties.

aft_outline shows the same blind spot. It lists interface ZodType< 34:168 as one entry and nothing underneath it, so an agent has no way to discover that parse, safeParse, parseAsync and friends live there.

Asking for callers of ZodType itself returns "0 callers", which is also wrong for a type that every schema in the package extends. I mention it because it may be the same root cause, but I didn't dig into it.

Reproduce:

git clone --depth 1 https://github.com/colinhacks/zod   # tested at 10dda3a3
# .cortexkit/aft.jsonc: { "tool_surface": "all", "callgraph_store": true, "search_index": true }
# wait for the callgraph store to finish (perf log: files=608 exports=2137 edges=55509)

callgraph callers  packages/zod/src/v4/classic/schemas.ts  parse      -> symbol_not_found
callgraph callers  packages/zod/src/v4/classic/schemas.ts  _parse     -> symbol_not_found
callgraph callers  packages/zod/src/v4/classic/schemas.ts  safeParse  -> symbol_not_found
callgraph callers  packages/zod/src/v4/classic/schemas.ts  ZodType    -> 0 callers
callgraph callers  packages/zod/src/v4/classic/parse.ts    parse      -> 1 caller, schemas.ts:298  (works)

Why it matters: I hit this while watching a model use AFT inside Pi. It reached for aft_callgraph first, got "not found", and fell back to sixteen grep calls to answer a question the call graph was supposed to answer in one. It got there in the end, but the tool that should have been the fast path was the one that failed.

I hit this both through the Pi plugin (hoisted tools, Pi 0.86.1 and 0.87.0) and by driving the binary directly through @cortexkit/aft-bridge, so it isn't a plugin-layer problem.

Diagnostics

Not run. The store had finished building; the plugin log shows perf tier2_callgraph_snapshot: source=callgraph_store files=608 exports=2137 edges=55509 before the failing calls, and the direct-bridge probe waited for callgraph_building to clear before asking.

Plugin version

@cortexkit/aft-pi 0.57.0

AFT binary version

0.57.0 (aft-darwin-arm64)

Platform

macOS, Apple Silicon

Log output (optional)

[aft] [ses_01a0c76c-...] callers: '_parse' failed after 2ms: callgraph extraction error: symbol '_parse' not found in .../packages/zod/src/v4/classic/schemas.ts
[aft] [ses_01a0c76c-...] callers: 'parse' failed after 0ms: callgraph extraction error: symbol 'parse' not found in .../packages/zod/src/v4/classic/schemas.ts

Activity

  1. aft-alfonso commented on Sep 22, 2026

    @aft-alfonso

    Reproduced, and you have diagnosed it more precisely than most reports manage. Both blind spots are real and they are one cause.

    A nine-line fixture is enough:

    export interface Shape {
      area(): number;
      scale(f: number): Shape;
    }
    export const helper = {
      compute: function doCompute(x: number) { return x * 2; },
      arrow: (y: number) => y + 1,
    };
    export function plain(n: number) { return n; }

    aft_outline returns exactly three symbols:

    E interface Shape 1:4
    E const helper = 5:8
    E function plain(...) 9:9
    

    No area, no scale, no compute, no doCompute, no arrow. aft_zoom on area fails the same way your parse did.

    The cause is in symbol extraction rather than in the call graph, which is why you saw a healthy graph with a hole in it. Our TypeScript extraction never visits method_signature nodes inside an interface, and never treats a function expression or arrow assigned to an object property as a definition. The call graph is built from those symbols, so anything invisible to extraction is unreachable to callers, zoom and outline alike. Your observation that a plain export const parse resolves correctly while the interface method and the object property do not is exactly the shape that predicts.

    Your third point needs a different answer, and I would rather separate it than let it ride along. callers of ZodType reporting zero is not the same defect. The call graph records calls, and interface X extends ZodType or const s: ZodType is a type reference, not a call site — so zero callers is literally true and unhelpful. That is a missing capability rather than a broken one. Worth its own issue if you want type references traversable; say so and I will open it with your context rather than folding it in here silently.

    The part of your report I found most useful is the last paragraph. A model reaching for aft_callgraph first, getting symbol_not_found, and falling back to sixteen greps is the real cost — the tool that should have been the fast path is the one that failed, and it failed in a way that looks like absence rather than a gap. That is worse than being slow.

    Fix dispatched. I will comment here when it lands and again when a release carries it.

  2. aft-alfonso commented on Sep 22, 2026

    @aft-alfonso

    Fixed in v0.57.1.

    Your 9-line fixture reproduced it exactly. Two node kinds were never extracted:

    • method_signature — method declarations inside an interface
    • methods and arrow properties declared as object literal properties

    So those symbols were absent from aft_outline, from the symbol cache, and from anything downstream that resolves against extracted symbols.

    One detail worth flagging since it changes behaviour beyond the fix itself: because these symbols now exist, callgraph and search indexes re-extract on first run after upgrading. That is a one-time background pass, not a rebuild you need to trigger.

    Thanks for the minimal fixture — it made the missing node kinds obvious rather than something to guess at.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions