You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ran mex against a large (1600+ file) real-world Rails codebase with a Ruby extractor I contributed (Prism-based). Found three related issues while comparing mex against plain grep on real lookup tasks, with a blind eval to back it up.
1. graph scope's exact-match scoring has no stopword filtering
taskTokens() in src/graph/scope.ts splits the free-text task string on non-identifier chars and gives every token >=2 chars a flat 1.0 "exact-name-match" boost if it happens to collide with a real (often trivial) identifier in the repo — e.g. task phrasing containing "on", "to", "of", "up" collided with Ruby DSL params/block args literally named that, burying the actually-relevant symbol under stopword noise.
Repro: mex graph scope "find every call site that invokes mark_failed! on a model" returned on (score 1.0) x5 as top hits; the real target never appeared in the top 10.
I have a fix (stopword list + filter, with a regression test) ready as a diff/PR if useful.
2. who-calls/scope can't find dynamically-generated methods, with no documented recovery path
Ruby methods generated via metaprogramming (e.g. a state-machine DSL block) have real call sites but no literal def, so no resolved node exists. who-calls <method> returns TARGET_NOT_FOUND with no next step. The call sites are captured, just in the unresolved_refs table, which isn't exposed by any CLI command — recovering them required hand-writing SQL against internal schema that the contributor docs say requires a core/discuss-first issue to even touch.
Would who-calls falling through to unresolved_refs when no resolved node exists (clearly labeled as unresolved/dynamic) be in scope for a lighter-weight contribution?
3. The benchmark doesn't compare against grep, and the README implies it does
evaluate/README.md says outright:
The current harness does not compare an agent with the graph against the same agent using only Read/Grep/Glob. Therefore these results do not support an end-to-end graph-vs-no-graph token-savings claim.
That's accurate and appreciated, but the top-level README's "Results" table (10.74x, 916x) reads as exactly that claim to a new user, and it's measured against a favorable synthetic baseline ("grep's top-3 whole files") rather than a real grep-only agent run.
Our eval
Six blind subagents (fresh context each, no shared answers, no knowledge of the "correct" result), split into a grep-only condition and a mex condition, on the same three real questions against our codebase. Real reported token/tool-call/time usage per agent:
Task
Condition
Tokens
Tool calls
Time
Correct?
Find every class defining build_commands! (excl. tests)
grep
36,719
1
10.3s
19/19
mex (graph query/sqlite)
61,736
2
27.5s
19/19
Find every call site of .mark_failed! in app/
grep
36,700
1
8.1s
15/23
mex (who-calls + unresolved_refs)
63,481
5
46.3s
23/23
Trace CoderSession's superclass chain to the gem boundary
grep
47,714
4
63.1s
correct
mex (graph scope + sqlite)
62,722
4
45.1s
correct
Takeaways:
mex ran 1.3-1.7x grep's tokens on every task, and matched or lost on wall time too.
mex tied grep on 2/3 tasks (no benefit for the extra cost) and won decisively on 1/3: grep's \.mark_failed! regex missed 8 real call sites that invoke the method without an explicit dot-receiver (bare self-calls inside the model classes); mex's AST-based edges don't have that blind spot. That's a real, non-obvious correctness edge — a naive regex can produce a confidently wrong "15 results, looks complete" answer that nothing flags.
Getting that win required falling through to raw sqlite against unresolved_refs (issue Expand KNOWN_RUNTIMES allowlist in dependency checker #2 above) — not something a first-time CLI user would discover from who-calls alone returning TARGET_NOT_FOUND.
Net: mex earns its cost specifically on call-site enumeration where call syntax varies in ways a regex can't enumerate up front. For straightforward name/definition lookups and short inheritance walks, grep was cheaper and just as correct in this test. Worth either softening the README's headline claim or running the real 3-arm comparison the eval docs already call out as missing.
Happy to share the Ruby extractor, the scope.ts stopword fix, and the raw eval transcripts if useful.
Ran mex against a large (1600+ file) real-world Rails codebase with a Ruby extractor I contributed (Prism-based). Found three related issues while comparing
mexagainst plain grep on real lookup tasks, with a blind eval to back it up.1.
graph scope's exact-match scoring has no stopword filteringtaskTokens()insrc/graph/scope.tssplits the free-text task string on non-identifier chars and gives every token >=2 chars a flat1.0"exact-name-match" boost if it happens to collide with a real (often trivial) identifier in the repo — e.g. task phrasing containing "on", "to", "of", "up" collided with Ruby DSL params/block args literally named that, burying the actually-relevant symbol under stopword noise.Repro:
mex graph scope "find every call site that invokes mark_failed! on a model"returnedon(score 1.0) x5 as top hits; the real target never appeared in the top 10.I have a fix (stopword list + filter, with a regression test) ready as a diff/PR if useful.
2.
who-calls/scopecan't find dynamically-generated methods, with no documented recovery pathRuby methods generated via metaprogramming (e.g. a state-machine DSL block) have real call sites but no literal
def, so no resolved node exists.who-calls <method>returnsTARGET_NOT_FOUNDwith no next step. The call sites are captured, just in theunresolved_refstable, which isn't exposed by any CLI command — recovering them required hand-writing SQL against internal schema that the contributor docs say requires a core/discuss-first issue to even touch.Would
who-callsfalling through tounresolved_refswhen no resolved node exists (clearly labeled as unresolved/dynamic) be in scope for a lighter-weight contribution?3. The benchmark doesn't compare against grep, and the README implies it does
evaluate/README.mdsays outright:That's accurate and appreciated, but the top-level README's "Results" table (10.74x, 916x) reads as exactly that claim to a new user, and it's measured against a favorable synthetic baseline ("grep's top-3 whole files") rather than a real grep-only agent run.
Our eval
Six blind subagents (fresh context each, no shared answers, no knowledge of the "correct" result), split into a grep-only condition and a mex condition, on the same three real questions against our codebase. Real reported token/tool-call/time usage per agent:
build_commands!(excl. tests)graph query/sqlite).mark_failed!in app/who-calls+unresolved_refs)CoderSession's superclass chain to the gem boundarygraph scope+ sqlite)Takeaways:
\.mark_failed!regex missed 8 real call sites that invoke the method without an explicit dot-receiver (bare self-calls inside the model classes); mex's AST-based edges don't have that blind spot. That's a real, non-obvious correctness edge — a naive regex can produce a confidently wrong "15 results, looks complete" answer that nothing flags.unresolved_refs(issue Expand KNOWN_RUNTIMES allowlist in dependency checker #2 above) — not something a first-time CLI user would discover fromwho-callsalone returningTARGET_NOT_FOUND.Net: mex earns its cost specifically on call-site enumeration where call syntax varies in ways a regex can't enumerate up front. For straightforward name/definition lookups and short inheritance walks, grep was cheaper and just as correct in this test. Worth either softening the README's headline claim or running the real 3-arm comparison the eval docs already call out as missing.
Happy to share the Ruby extractor, the
scope.tsstopword fix, and the raw eval transcripts if useful.