Skip to content

fix(callers): quote and count every call site inside a caller - #406

Open
TigerkidYang wants to merge 2 commits into
trailhq:mainfrom
TigerkidYang:fix/callers-all-sites
Open

TigerkidYang wants to merge 2 commits into
trailhq:mainfrom
TigerkidYang:fix/callers-all-sites

Conversation

@TigerkidYang

Copy link
Copy Markdown

Fixes #363.

What

The graph keeps one edge per (caller, relation, callee), so a function that calls the symbol three times was listed once by graft callers, with only its first call site quoted and nothing saying the others existed. Counting call sites off that list gives 7 for a true 9, as the report shows.

  • referenceLines() (new, next to referenceLine() in src/blast/evidence.ts) returns every line in the caller's span that references the symbol.
  • graft callers quotes each of them and tags the hit with · N sites when there is more than one (sites, not call sites: the walk also follows references/imports/extends edges).
  • --json hits gain an optional sites: number[] — line numbers only; the quoted text stays out of the data contract, as the existing test insists.
  • A recursive function is its own caller; its declaration line names the symbol but is not a site, so it is dropped.
  • hitLine() keeps its old signature (an optional quote, now also accepting an array), so the MCP tool and workspace renderers are unchanged.

Known approximation

Line matching reuses the same word-boundary regex the existing single quote uses, so a comment naming the symbol counts as a site and two calls on one line count once. Recording call positions at extraction time (extract.ts has the tree-sitter node in hand when it emits the calls RawEdge) and aggregating them onto EdgeV1 during resolve would make this exact and relation-aware; that is a graph-schema change, so I left it as a follow-up rather than folding it in here.

Testing

  • New test: three sites in one caller are quoted and counted, a single site carries no count, JSON sites is [5, 6, 7] / [10] and contains no quoted text, and a recursive caller's declaration line is not a site.
  • npm test in a node:22-bookworm container: 1222 tests, 1217 passed, 0 failed (the rest skipped); tsc --noEmit clean.

AI disclosure

Implemented with Claude (Claude Code); I reviewed the change and ran the suite.

🤖 Generated with Claude Code

The graph keeps one edge per calling function, so a function that calls the
symbol three times was listed once with only its first call site quoted, and
nothing said the others existed (trailhq#363). Quote every matching line in the
caller's span, tag the hit with the site count when there is more than one,
and expose the site line numbers as `sites` in --json (quoted text stays out
of the JSON contract).
…own declaration

Review follow-ups: the tag reads `· N sites` because the walk also follows
references/imports/extends edges; a recursive function is its own caller
and its declaration line named the symbol without being a site, so it is
dropped; the JSON `sites` doc states when the key is absent.
@trailhq-graft

trailhq-graft Bot commented Sep 16, 2026

Copy link
Copy Markdown

🌱 graft blast radius

2 areas changed → 3 areas can be affected. 6 dependent symbols, depth 2.
Tests: no test reaches Callers Command, Evidence Collection.
Tag: @anirudhkumar-nanonets — 5 of 5 areas · @shhdwi — 4 of 5 areas

flowchart TB
  A0(("Tool Invocation<br/>3 symbols"))
  A1(("Workspace Federation<br/>2 symbols"))
  A2(("CLI Entry Point<br/>1 symbol"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2 reached;
Loading
Can be affected Symbols Nearest hop Reached from
Tool Invocation 3 src/mcp/tools.ts:L132-L147 renderMatches — calls, depth 1 Callers Command
Workspace Federation 2 src/graph/workspace.ts:L659-L695 federateCallers — calls, depth 1 Callers Command
CLI Entry Point 1 src/cli.ts:L1-L1407 cli.ts — calls, depth 1 Callers Command
Who knows this code — 2 people across 5 areas
Area Who knows it
Callers Command · changed @anirudhkumar-nanonets — 5 commits, last 23d ago · @shhdwi — 4 commits, last 2mo ago
Evidence Collection · changed @anirudhkumar-nanonets — 1 commit, last 23d ago
Tool Invocation · affected @shhdwi — 14 commits, last 1mo ago · @anirudhkumar-nanonets — 7 commits, last 14d ago
Workspace Federation · affected @shhdwi — 6 commits, last 1mo ago · @anirudhkumar-nanonets — 3 commits, last 1mo ago
CLI Entry Point · affected @anirudhkumar-nanonets — 39 commits, last 6d ago · @shhdwi — 23 commits, last 1mo ago

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 6 dependent symbols, grouped by area

Tool Invocation — 3 symbols in 1 file

  • src/mcp/tools.ts:L132-L147 — renderMatches (calls, depth 1)
    143: else for (const h of hits) lines.push(hitLine(direction, h, showDepth));
  • src/mcp/tools.ts:L153-L199 — callWorkspaceTool (calls, depth 2)
  • src/mcp/tools.ts:L247-L329 — callSingleTool (calls, depth 2)

Workspace Federation — 2 symbols in 2 files

  • src/graph/workspace.ts:L659-L695 — federateCallers (calls, depth 1)
    681: else for (const h of hits) lines.push(hitLine(direction, h, showDepth));
  • src/graph/workspace-cli.ts:L132-L144 — runWorkspaceCallers (calls, depth 2)

CLI Entry Point — 1 symbol in 1 file

  • src/cli.ts:L1-L1407 — cli.ts (calls, depth 1)
    810: const { runCallersCommand } = await import("./graph/traverse-cli.js");
Test signal per changed area — 2 ✗

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • Callers Command — 0 of 4 reached · no test file reaches it
    • not reached: hitLine, quotesFor, hitJson, runCallersCommand
  • Evidence Collection — 0 of 1 reached · no test file reaches it
    • not reached: referenceLines
1 test suite also references this code

1 symbol, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/workspace.test.ts

graft blast · origin/main...HEAD · depth 2 · 3 changed files

Open the interactive graph → — click an area to see its dependent symbols at file:line.

github-actions Bot added a commit that referenced this pull request Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

callers quotes only the first call site inside each calling function, so the call-site list silently undercounts

1 participant