mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-09-19 07:34:57 +08:00
2777bb8dae
Multi-pronged fix to make codegraph competitive on Go multi-module repos
(cosmos-sdk, etcd) where it previously lost or tied. Driven by an 8-question
agent-eval audit across cobra, gin, prometheus, cosmos-sdk, and etcd: the
baseline had codegraph losing ~60% on cost on cosmos-sdk and mixed on etcd
deep cross-module flows, while winning cleanly on the single-module and
non-protobuf-heavy repos.
Diagnostics ruled OUT `go.work` parsing as the gap (prometheus crushes
without it). The actual failure modes were generated-file noise warping
disambiguation, missing gRPC interface→impl bridge in structural-typing Go,
and trace's failure path triggering 3-5 follow-up tool calls instead of
inlining the material the agent needed.
Changes:
- New `src/extraction/generated-detection.ts` — path-pattern classifier
for `.pb.go`, `.pulsar.go`, `_grpc.pb.go`, `_mock.go`, `_mocks.go`,
`mock_*.go`, `.generated.[jt]sx?`, `_pb2(_grpc)?.py`, `.pb.{cc,h}`,
`.g.dart`, `.freezed.dart`. Applied as a stable sort tiebreaker in
`findSymbol`, `findAllSymbols`, `codegraph_search` (MCP + CLI),
`codegraph_explore` file ranking, and context formatter Entry Points /
Related Symbols / Code blocks. Cosmos's `msgServer.Send` now ranks #3
instead of #9 on a `Send` search.
- New `goGrpcStubImplEdges` synthesizer in `callback-synthesizer.ts` —
detects `UnimplementedXxxServer` structs in generated files, identifies
their RPC methods (excluding `mustEmbed*` / `testEmbeddedByValue` gRPC
markers), and emits `calls` edges to the matching methods on any
non-generated struct whose method-name set is a superset. Closes Go's
structural-typing gap that the existing `interfaceOverrideEdges` (Java /
Kotlin only) couldn't bridge. 467 bridge edges on cosmos-sdk; bank's
`UnimplementedMsgServer::Send` points to `x/bank/keeper/msg_server.go`
only, not to `msgClient` siblings or mock files.
- Trace-failure rewrite (`handleTrace`) — when no static path connects
endpoints, instead of telling the agent to call `codegraph_node` (a
3-4-call fan-out), inline both endpoints' bodies (120 lines / 3600 chars
per endpoint), their callers (≤6), and callees (≤8) in one response.
- Trace endpoint-pairing improvements — scores every `from`×`to`
candidate combo by shared directory prefix and tries the best-paired
pair first (the full candidate set, not just FTS top-5). A
less-canonical-path penalty (`enterprise/`, `contrib/`, `examples/`,
`vendor/`, `third_party/`, `deprecated/`, `legacy/`) ensures the
canonical-module pair wins even when a side-experiment shares more of
its directory prefix. Find-path probe budget capped at 20 pairs.
- Test-file deprioritization in `codegraph_explore` `isLowValue` — adds
suffix patterns (`_test.go`, `_spec.rb`, `.test.ts`, `.spec.tsx`,
`Test.java`, `Spec.kt`) alongside the existing directory-style patterns.
Otherwise etcd's `watchable_store_test.go` consumes 5K chars of explore
budget that should go to the hand-written flow source.
Tests:
- New `__tests__/generated-detection.test.ts` (4 unit tests) pins the
suffix patterns.
- New "Go gRPC stub→impl synthesis" integration test suite in
`frameworks-integration.test.ts` (2 tests): positive bridge from stub
to hand-written impl, AND the precision case (don't bridge to a
generated sibling like `msgClient` in the same .pb.go).
- Full suite: 1076/1076 pass.
Empirical (post-fix, n=2 average per question):
| Repo / Q | WITH | WITHOUT | Reads (W/WO) | Time (W/WO)
|-------------------------|------------|-------------|--------------|------------
| cobra (parse cmds) | $0.27 | $0.27 | 0 / 4 | 39s / 60s
| prometheus (scrape→TSDB)| $0.63 | $0.70 | 0 / 6 | 106s/143s
| cosmos-sdk Q1 (MsgSend) | $0.41 | $0.26 | 1 / 2 | 67s / 64s
| cosmos-sdk Q2 (Delegate)| $0.47 | $0.46 | 0 / 5 | 50s / 73s
| cosmos-sdk Q3 (gov tally)| $0.34 | $0.31 | 1.5 / 3 | 54s / 76s
| etcd Q1 (Put→raft) | $0.65 | $0.78 | 0 / 4 | 98s / 129s
| etcd Q2 (watch) | $0.36 | $0.50 | 0 / 4+ | 58s / 89s
Codegraph wins on reads + time on every question. Cost is mixed: 3 clean
wins, 3 tied (within 10%), 1 stubborn cost loss on the grep-favored Q1.
Compared to baseline, the cosmos-sdk cost-gap collapsed from -60% to -15%
on average, and Q3 went from a 75% loss to a tie. Raw run artifacts in
`/tmp/cg-finalv2-*/` and `/tmp/cg-final-*/`.
Memory written at `project_go_multi_module_audit.md` for the methodology
+ before/after numbers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
2.2 KiB
TypeScript
48 lines
2.2 KiB
TypeScript
/**
|
|
* Regression coverage for the generated-file detector that drives
|
|
* symbol-disambiguation down-ranking. Locked here because the suffix
|
|
* list is a contract: if a future edit drops `.pb.go`, the cosmos-sdk
|
|
* trace endpoint regresses to the gRPC stub (see
|
|
* `project_go_multi_module_audit` memory + the audit in #N/A).
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { isGeneratedFile } from '../src/extraction/generated-detection';
|
|
|
|
describe('isGeneratedFile', () => {
|
|
it('classifies Go protobuf / gRPC / pulsar / mock outputs as generated', () => {
|
|
expect(isGeneratedFile('api/cosmos/bank/v1beta1/tx_grpc.pb.go')).toBe(true);
|
|
expect(isGeneratedFile('x/bank/types/tx.pb.go')).toBe(true);
|
|
expect(isGeneratedFile('api/cosmos/bank/v1beta1/tx.pulsar.go')).toBe(true);
|
|
// cosmos-sdk uses `<base>_mocks.go`; mockgen's default is `mock_<src>.go`;
|
|
// many projects use `<base>_mock.go`. All three are mockgen output.
|
|
expect(isGeneratedFile('x/auth/testutil/expected_keepers_mocks.go')).toBe(true);
|
|
expect(isGeneratedFile('internal/foo_mock.go')).toBe(true);
|
|
expect(isGeneratedFile('mock_keeper.go')).toBe(true);
|
|
});
|
|
|
|
it('does not flag the hand-written keeper as generated', () => {
|
|
expect(isGeneratedFile('x/bank/keeper/msg_server.go')).toBe(false);
|
|
expect(isGeneratedFile('x/bank/keeper/send.go')).toBe(false);
|
|
});
|
|
|
|
it('catches common cross-language codegen suffixes', () => {
|
|
expect(isGeneratedFile('app/foo.generated.ts')).toBe(true);
|
|
expect(isGeneratedFile('app/foo.generated.tsx')).toBe(true);
|
|
expect(isGeneratedFile('proto/bar_pb2.py')).toBe(true);
|
|
expect(isGeneratedFile('proto/bar_pb2_grpc.py')).toBe(true);
|
|
expect(isGeneratedFile('lib/baz.pb.cc')).toBe(true);
|
|
expect(isGeneratedFile('lib/baz.pb.h')).toBe(true);
|
|
expect(isGeneratedFile('lib/quux.g.dart')).toBe(true);
|
|
expect(isGeneratedFile('lib/quux.freezed.dart')).toBe(true);
|
|
});
|
|
|
|
it('leaves ordinary source files alone', () => {
|
|
expect(isGeneratedFile('src/index.ts')).toBe(false);
|
|
expect(isGeneratedFile('src/components/Foo.tsx')).toBe(false);
|
|
expect(isGeneratedFile('lib/main.dart')).toBe(false);
|
|
expect(isGeneratedFile('cmd/server/main.go')).toBe(false);
|
|
expect(isGeneratedFile('app/db.py')).toBe(false);
|
|
});
|
|
});
|