Files
colbymchenry__codegraph/__tests__/generated-detection.test.ts
Colby McHenry 2777bb8dae feat(go): generated-file down-rank + gRPC stub-impl bridge + trace-failure inlining
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>
2026-05-27 02:28:19 -05:00

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);
});
});