Skip to content

Commit cdf1d42

Browse files
committed
feat: enable graph context by default
1 parent 32c6653 commit cdf1d42

9 files changed

Lines changed: 385 additions & 6 deletions

File tree

README.EN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
| Document | Purpose |
1616
|------|------|
1717
| [Docs index](./docs/README.md) | Unified entry for stable docs, plans, changelog, and archived delivery material |
18-
| [2026-04-09 update summary](./docs/changelog/2026-04-09.md) | Summary of the latest delivery-facing updates |
18+
| [2026-04-10 update summary](./docs/changelog/2026-04-10.md) | Default-on graph context for codebase retrieval plus docs sync |
1919
| [Latest delivery bundle](./docs/archive/deliveries/2026-04-09-index-and-memory/delivery-bundle.md) | Handoff bundle for the latest verified delivery |

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
## Updates
3535

36+
- `2026-04-10`: `codebase-retrieval` now includes the lightweight direct graph summary by default, with MCP metadata, tests, and changelog docs updated in sync.
3637
- `2026-04-06`: tightened the default user path, memory governance, and operational visibility to make first use, feedback loops, and health checks clearer.
3738
- `2026-04-07`: improved the indexing pipeline with lighter planning, snapshot copy reduction, queue observability, fallback hardening, and repeatable benchmarks.
3839
- `2026-04-08`: added the embedding gateway, local caching and multi-upstream routing, plus Hugging Face integration and MCP context lifecycle tools.
@@ -279,10 +280,11 @@ ContextAtlas focuses on **what context to provide**, not how the task should be
279280
| Document | Purpose |
280281
|------|------|
281282
| [Docs index](./docs/README.md) | Unified entry for stable docs, plans, changelog, and archived delivery material |
283+
| [2026-04-10 update summary](./docs/changelog/2026-04-10.md) | Default-on graph context for codebase retrieval plus MCP/docs sync |
282284
| [First use guide](./docs/guides/first-use.md) | Fast onboarding path for the default `contextatlas` loop |
283-
| [2026-04-09 update summary](./docs/changelog/2026-04-09.md) | Summary of index planning thresholds, long-term memory table split, and delivery sync |
284285
| [2026-04-06 update summary](./docs/changelog/2026-04-06.md) | Summary of the new main path, memory governance, operations, release gate, and team metrics |
285286
| [2026-04-07 update summary](./docs/changelog/2026-04-07.md) | Summary of the seven indexing phases covering lightweight planning, snapshot copy reduction, health repair, observability, fallback hardening, storage trimming, and benchmarks |
287+
| [2026-04-09 update summary](./docs/changelog/2026-04-09.md) | Summary of index planning thresholds, long-term memory table split, and delivery sync |
286288
| [Deployment guide](./docs/guides/deployment.md) | Installation, deployment patterns, MCP integration, operations |
287289
| [CLI reference](./docs/reference/cli.md) | CLI commands, categories, and examples |
288290
| [MCP reference](./docs/reference/mcp.md) | MCP tools, parameters, and calling patterns |

README_ZH.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
- `2026-04-07`:围绕索引链路完成轻量计划、快照复制优化、队列可观测、fallback 稳定性和性能基准建设。
3838
- `2026-04-08`:新增 embedding gateway、本地缓存与多上游切换能力,并补齐 Hugging Face 接入与 MCP 上下文生命周期工具。
3939
- `2026-04-09`:为索引计划增加 churn / cost 策略、把长期记忆迁到独立表 + FTS5,并完成默认路径硬化、阈值配置化、运维告警口径对齐与文档同步。
40+
- `2026-04-10``codebase-retrieval` 默认附带轻量直接图谱摘要,MCP 元数据、测试与更新日志同步到位。
4041

4142
## 目录
4243

@@ -282,6 +283,7 @@ ContextAtlas 更关注“给 agent 什么上下文”,而不是“替 agent
282283
| 文档 | 用途 |
283284
|------|------|
284285
| [文档总览](./docs/README.md) | 文档目录统一入口,区分稳定文档、计划、更新日志和归档材料 |
286+
| [2026-04-10 更新总结](./docs/changelog/2026-04-10.md) | `codebase-retrieval` 默认附带图谱摘要,并同步 MCP 文档与测试口径 |
285287
| [首次使用](./docs/guides/first-use.md) | 10 分钟跑通默认闭环,先理解包名、CLI 名和第一条查询 |
286288
| [2026-04-07 更新总结](./docs/changelog/2026-04-07.md) | 索引 7 个阶段优化总结,覆盖轻量计划、快照复制、健康修复、队列可观测性、fallback、存储裁剪与 benchmark |
287289
| [部署手册](./docs/guides/deployment.md) | 安装、部署场景、MCP 集成、运维建议 |

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
## 更新日志
4444

45+
- [2026-04-10](./changelog/2026-04-10.md)
4546
- [2026-04-09](./changelog/2026-04-09.md)
4647
- [2026-04-08](./changelog/2026-04-08.md)
4748
- [2026-04-07](./changelog/2026-04-07.md)

docs/changelog/2026-04-10.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# ContextAtlas 更新总结(2026-04-10)
2+
3+
本轮更新围绕一个很具体的收口动作展开:
4+
5+
> `codebase-retrieval` 的轻量图谱摘要从“可选增强”升级为“默认带出”,降低调用方的参数负担,同时保持显式关闭能力。
6+
7+
## 一、本轮完成了什么
8+
9+
这次主要完成了 4 个同步动作:
10+
11+
1. `codebase-retrieval``include_graph_context` 默认值改为开启
12+
2. MCP 工具注册元数据同步改为默认开启
13+
3. 回归测试更新为覆盖“默认开启”口径
14+
4. 今日更新文档与 README / docs 索引同步
15+
16+
## 二、默认行为调整
17+
18+
此前这项能力属于轻集成:
19+
20+
- 默认不启用
21+
- 调用方显式传 `include_graph_context: true` 时,才会从 top seed symbol 抽取少量符号
22+
- 再查询这些符号的直接 upstream / downstream
23+
- 最后在 text / json 返回里附带一段轻量图谱摘要
24+
25+
本轮调整后:
26+
27+
- 默认值改为 `true`
28+
- 现有调用方即使不传该参数,也会拿到轻量图谱摘要
29+
- 如果某些客户端明确不需要该摘要,仍可传 `include_graph_context: false` 关闭
30+
31+
这样做的目的很直接:
32+
33+
- 让常见检索调用默认更完整
34+
- 减少客户端 prompt / wrapper 里重复补参数
35+
- 保持响应体规模仍然可控,只追加 top symbols 的直接邻接摘要,不扩成重图查询
36+
37+
## 三、涉及文件
38+
39+
- `src/mcp/tools/codebaseRetrieval.ts`
40+
- `src/mcp/registry/tools.ts`
41+
- `tests/codebase-retrieval.test.ts`
42+
- `docs/reference/mcp.md`
43+
44+
另外同步更新了:
45+
46+
- `docs/README.md`
47+
- `README.md`
48+
- `README.EN.md`
49+
- `README_ZH.md`
50+
51+
## 四、验证口径
52+
53+
本次改动沿用现有验证门槛:
54+
55+
```bash
56+
pnpm build
57+
node --import tsx --test tests/codebase-retrieval.test.ts
58+
```
59+
60+
说明:
61+
62+
- 与本次改动直接相关的 build 与工具测试应继续通过
63+
- `mcp-stdio` 在混合批跑场景下仍有既有不稳定现象,因此这次仍不把它作为该轻集成默认开启的放行门槛

docs/reference/mcp.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ contextatlas mcp
129129

130130
### 代码检索
131131

132+
`codebase-retrieval` 现在默认会附带一段轻量直接图谱摘要;只有在你明确不需要时,才传 `include_graph_context: false` 关闭。
133+
132134
```json
133135
{
134136
"repo_path": "/path/to/repo",

src/mcp/registry/tools.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ Examples of BAD queries:
9898
description: 'Whether to return a lightweight overview or the expanded full retrieval payload',
9999
default: 'expanded',
100100
},
101+
include_graph_context: {
102+
type: 'boolean',
103+
description: 'Whether to append a compact direct graph context summary for top matched symbols.',
104+
default: true,
105+
},
101106
},
102107
required: ['repo_path', 'information_request'],
103108
},

src/mcp/tools/codebaseRetrieval.ts

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import os from 'node:os';
1414
import path from 'node:path';
1515
import { z } from 'zod';
1616
import { responseFormatSchema } from './responseFormat.js';
17-
import { generateProjectId } from '../../db/index.js';
17+
import { closeDb, generateProjectId, initDb } from '../../db/index.js';
18+
import { GraphStore } from '../../graph/GraphStore.js';
1819
import type {
1920
BlockFirstPayload,
2021
CheckpointCandidate,
@@ -57,6 +58,11 @@ export const codebaseRetrievalSchema = z.object({
5758
.optional()
5859
.default('expanded')
5960
.describe('Whether to return a lightweight overview or the expanded full retrieval payload'),
61+
include_graph_context: z
62+
.boolean()
63+
.optional()
64+
.default(true)
65+
.describe('When true, append a compact direct graph context summary for top matched symbols.'),
6066
});
6167

6268
export type CodebaseRetrievalInput = z.infer<typeof codebaseRetrievalSchema>;
@@ -144,12 +150,24 @@ interface RetrievalResultCard {
144150
reasoning: string[];
145151
trustRules: string[];
146152
nextActions: string[];
153+
graphContext?: RetrievalGraphContextSummary;
147154
status?: {
148155
headline: string;
149156
details: string[];
150157
};
151158
}
152159

160+
interface RetrievalGraphSymbolSummary {
161+
name: string;
162+
filePath: string;
163+
directUpstream: string[];
164+
directDownstream: string[];
165+
}
166+
167+
interface RetrievalGraphContextSummary {
168+
symbols: RetrievalGraphSymbolSummary[];
169+
}
170+
153171
// ===========================================
154172
// 自动索引逻辑
155173
// ===========================================
@@ -323,7 +341,14 @@ export async function handleCodebaseRetrieval(
323341
args: CodebaseRetrievalInput,
324342
onProgress?: ProgressCallback,
325343
): Promise<{ content: Array<{ type: 'text'; text: string }> }> {
326-
const { repo_path, information_request, technical_terms, response_format, response_mode } = args;
344+
const {
345+
repo_path,
346+
information_request,
347+
technical_terms,
348+
response_format,
349+
response_mode,
350+
include_graph_context,
351+
} = args;
327352
const requestId = crypto.randomUUID().slice(0, 8);
328353
const startedAt = Date.now();
329354
const reportProgress = createRetrievalProgressReporter(onProgress);
@@ -644,6 +669,7 @@ export async function handleCodebaseRetrieval(
644669
informationRequest: information_request,
645670
technicalTerms: technical_terms || [],
646671
pack: contextPack,
672+
includeGraphContext: include_graph_context ?? true,
647673
status: {
648674
headline: '索引状态: 完整模式已就绪',
649675
details: ['当前结果来自已建立索引的混合检索链路'],
@@ -683,6 +709,7 @@ function formatMcpResponse(
683709
? {
684710
responseMode: options.responseMode,
685711
summary: overview.summary,
712+
graphContext: resultCard.graphContext,
686713
topFiles: overview.topFiles,
687714
contextBlocks,
688715
references: overview.references,
@@ -698,6 +725,7 @@ function formatMcpResponse(
698725
files: files.length,
699726
totalSegments: files.reduce((acc, f) => acc + f.segments.length, 0),
700727
},
728+
graphContext: resultCard.graphContext,
701729
contextBlocks,
702730
references: contextBlocks.flatMap((block) => block.provenance.map((item) => ({ blockId: block.id, ...item }))),
703731
expansionCandidates: overview.expansionCandidates,
@@ -730,6 +758,13 @@ function formatMcpResponse(
730758
'',
731759
'### Next Inspection Suggestions',
732760
...(overview.nextInspectionSuggestions.length > 0 ? overview.nextInspectionSuggestions.map((item) => `- ${item}`) : ['- None']),
761+
...(resultCard.graphContext
762+
? [
763+
'',
764+
'### Graph Context',
765+
...formatGraphContextSummary(resultCard.graphContext),
766+
]
767+
: []),
733768
];
734769
return { content: [{ type: 'text', text: lines.join('\n') }] };
735770
}
@@ -768,6 +803,13 @@ function formatMcpResponse(
768803
'### 相关长期记忆 (Source: Long-term Memory)',
769804
formatLongTermMemoryMatches(resultCard.longTermMemories),
770805
'',
806+
...(resultCard.graphContext
807+
? [
808+
'### 直接图谱摘要 (Source: Code Graph)',
809+
formatGraphContextSummary(resultCard.graphContext).join('\n'),
810+
'',
811+
]
812+
: []),
771813
'### 近期反馈信号 (Source: Feedback Loop)',
772814
formatFeedbackMatches(resultCard.feedbackSignals),
773815
'',
@@ -854,12 +896,14 @@ async function buildRetrievalResultCard({
854896
informationRequest,
855897
technicalTerms,
856898
pack,
899+
includeGraphContext,
857900
status,
858901
}: {
859902
repoPath: string;
860903
informationRequest: string;
861904
technicalTerms: string[];
862905
pack: ContextPack;
906+
includeGraphContext: boolean;
863907
status?: RetrievalResultCard['status'];
864908
}): Promise<RetrievalResultCard> {
865909
try {
@@ -900,11 +944,16 @@ async function buildRetrievalResultCard({
900944
),
901945
);
902946

947+
const graphContext = includeGraphContext
948+
? buildGraphContextSummary(repoPath, pack)
949+
: undefined;
950+
903951
return {
904952
memories: memoryMatchesWithFeedback,
905953
decisions: decisionMatches,
906954
longTermMemories: longTermMatches,
907955
feedbackSignals,
956+
graphContext,
908957
reasoning: buildReasoningLines(
909958
informationRequest,
910959
technicalTerms,
@@ -929,6 +978,7 @@ async function buildRetrievalResultCard({
929978
decisions: [],
930979
longTermMemories: [],
931980
feedbackSignals: [],
981+
graphContext: includeGraphContext ? buildGraphContextSummary(repoPath, pack) : undefined,
932982
reasoning: buildReasoningLines(informationRequest, technicalTerms, pack, [], [], [], []),
933983
trustRules: buildTrustRules(),
934984
nextActions: buildNextActions({
@@ -941,6 +991,81 @@ async function buildRetrievalResultCard({
941991
}
942992
}
943993

994+
function buildGraphContextSummary(
995+
repoPath: string,
996+
pack: ContextPack,
997+
): RetrievalGraphContextSummary | undefined {
998+
const projectId = generateProjectId(repoPath);
999+
const snapshotId = resolveCurrentSnapshotId(projectId);
1000+
const db = initDb(projectId, snapshotId);
1001+
1002+
try {
1003+
const store = new GraphStore(db);
1004+
const seen = new Set<string>();
1005+
const symbols: RetrievalGraphSymbolSummary[] = [];
1006+
1007+
for (const seed of pack.seeds) {
1008+
const symbolName = extractSymbolNameFromBreadcrumb(seed.record.breadcrumb);
1009+
if (!symbolName) continue;
1010+
1011+
const matches = store
1012+
.findSymbolsByName(symbolName)
1013+
.filter((symbol) => symbol.filePath === seed.filePath);
1014+
const match = matches[0];
1015+
if (!match) continue;
1016+
if (seen.has(match.id)) continue;
1017+
seen.add(match.id);
1018+
1019+
const upstream = store
1020+
.getDirectRelations(match.id, 'upstream')
1021+
.slice(0, 3)
1022+
.map((relation) => `${relation.relationType}:${relation.targetName}`);
1023+
const downstream = store
1024+
.getDirectRelations(match.id, 'downstream')
1025+
.slice(0, 3)
1026+
.map((relation) => `${relation.relationType}:${relation.targetName}`);
1027+
1028+
symbols.push({
1029+
name: match.name,
1030+
filePath: match.filePath,
1031+
directUpstream: upstream,
1032+
directDownstream: downstream,
1033+
});
1034+
1035+
if (symbols.length >= 3) {
1036+
break;
1037+
}
1038+
}
1039+
1040+
return symbols.length > 0 ? { symbols } : undefined;
1041+
} catch (err) {
1042+
logger.debug({ error: (err as Error).message }, '构建图谱摘要失败,忽略 code graph summary');
1043+
return undefined;
1044+
} finally {
1045+
closeDb(db);
1046+
}
1047+
}
1048+
1049+
function extractSymbolNameFromBreadcrumb(breadcrumb: string): string | null {
1050+
const parts = breadcrumb.split(' > ');
1051+
const tail = parts[parts.length - 1];
1052+
if (!tail || tail.includes('/')) {
1053+
return null;
1054+
}
1055+
1056+
return tail
1057+
.replace(/^(abstract class |class |interface |fn\*? |def |func |struct |enum |trait |record |@interface )/, '')
1058+
.trim() || null;
1059+
}
1060+
1061+
function formatGraphContextSummary(summary: RetrievalGraphContextSummary): string[] {
1062+
return summary.symbols.flatMap((symbol) => [
1063+
`- ${symbol.name} (${symbol.filePath})`,
1064+
` upstream: ${symbol.directUpstream.length > 0 ? symbol.directUpstream.join(', ') : 'none'}`,
1065+
` downstream: ${symbol.directDownstream.length > 0 ? symbol.directDownstream.join(', ') : 'none'}`,
1066+
]);
1067+
}
1068+
9441069
function rankFeatureMemoryMatches(
9451070
memories: FeatureMemory[],
9461071
informationRequest: string,

0 commit comments

Comments
 (0)