Skip to content

find-callers misses cross-file callers in .tsx views: exported exportSvg helpers report zero callers #38

Description

@jpwinans

Summary

find-callers on the exported helpers in tapestry/src/lib/exportSvg.ts returns empty or intra-file-only caller lists, even though all four Sigma views call them. Cross-file calls edges from the .tsx views are missing from the graph. Intra-file call edges and file-level import edges for the same symbols are correct, so the gap is specific to symbol-level call extraction across files.

Reproduction

Against codebase-the-loom (map commit 0343de0; none of the files below changed between that commit and current HEAD, so this is not map staleness):

loom find-callers '{"name": "exportSvgFile (exportSvg)", "graph": "codebase-the-loom"}'
# → "callers": []

loom find-callers '{"name": "exportPngFile (exportSvg)", "graph": "codebase-the-loom"}'
# → "callers": []

loom find-callers '{"name": "graphToSvg (exportSvg)", "graph": "codebase-the-loom"}'
# → 1 caller: exportSvgFile at tapestry/src/lib/exportSvg.ts:268 (intra-file only)

loom find-callers '{"name": "downloadBlob (exportSvg)", "graph": "codebase-the-loom"}'
# → 2 callers: exportSvg.ts:269, exportSvg.ts:322 (intra-file only)

Actual call sites in the tree:

Symbol Reported callers Missing real call sites
exportSvgFile none Explorer.tsx:416, Chronicle.tsx:526, SemanticView.tsx:425, SystemsView.tsx:446ff
exportPngFile none Explorer.tsx:396, Chronicle.tsx:515, SemanticView.tsx:409, SystemsView.tsx:446
downloadBlob intra-file ×2 Explorer.tsx:687
exportFilename intra-file only Explorer.tsx:398/425/689 + equivalents in the other three views

Each view imports the helpers as destructured named imports, e.g. Explorer.tsx:33:

import { downloadBlob, exportFilename, exportPngFile, exportSvgFile } from "../../lib/exportSvg";

What still works

  • Intra-file calls edges in exportSvg.ts resolve (e.g. exportSvgFile → graphToSvg).
  • File-level imports are correct: loom explore '{"name": "file:tapestry/src/lib/exportSvg.ts"}' lists all 5 importers (the four views plus the test file).
  • blast-radius on the file still reaches the views through file-level requires edges, so file-scoped queries are unaffected.

Possible discriminators

Every missing call site shares three properties, any of which could be the trigger in the tree-sitter call-edge extraction:

  1. caller lives in a .tsx file (the resolving intra-file callers are in a .ts file);
  2. callee arrives via a destructured named import rather than being defined in-file;
  3. the call happens inside a nested arrow function (useCallback body / event handler) inside a React component, so the enclosing symbol attribution may fail. Some sites are additionally void-wrapped (void exportPngFile(...)), but exportSvgFile calls are not and are still missing, so void alone is not the discriminator.

Impact

A zero-callers result reads as "unused, safe to rename or delete" — exactly backwards here. docs/architecture/QUERYING.md steers agents to query the graph before grepping, so this can directly mislead automated consumers into unsafe edits on the frontend helpers.

Workaround

Treat a zero-callers result as inconclusive: corroborate with explore's importedBy list or a direct file read before concluding a symbol is unused.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions