# Graph Builder compatibility pipeline / schema / persistence semantics

> 任务：列出 `knowledge-graph.json` / `meta.json` / `fingerprints.json` 的字段结构、full 与 incremental 流水线的输入输出与不变量、前端（dashboard）直接消费约束，并明确「必须确定性复刻」与「可后续 LLM/agent 增强」的边界。
>
> **修订 2026-07-02：** 原文的 node type 数量表述有误；UA `schema.ts` 的 `GraphNodeSchema.type` 枚举实际是 **21 种**（5 code + 5 non-code-file + 3 infra/schema + 3 domain + 5 knowledge），与 `types.ts` 的 `NodeType` 一致。下方已更正。
>
> 数据源：`/Users/lonng/devel/ai/Understand-Anything/understand-anything-plugin`（commit 取自本调研时刻），重点 `packages/core/src`、`packages/dashboard/src/App.tsx`、`skills/understand/SKILL.md`。

---

## 0. 文件布局（项目内产物）

仓库根下固定目录 `.agentteam-insight/`：

| 文件 | 角色 | 生产者 | 消费者 |
|---|---|---|---|
| `knowledge-graph.json` | 主产物，dashboard 唯一必需的图 | Phase 7 SAVE | dashboard `App.tsx`、`/understand-*` 系列 skill |
| `meta.json` | 最近一次分析的元信息（commit、时间戳、文件数、可选 theme） | Phase 7 SAVE | dashboard `App.tsx`（theme）、下次运行 Phase 0 决策 |
| `fingerprints.json` | 结构指纹基线，用于增量分级 | Phase 7 `build-fingerprints.mjs` | 下次运行的 `analyzeChanges` |
| `config.json` | 用户偏好（autoUpdate、outputLanguage） | `/understand --auto-update` / `--language` | dashboard、Phase 0 |
| `.agentteam-insightignore` | 文件过滤规则（gitignore 风格） | Phase 0.5 `generate-ignore.mjs` | Phase 1 SCAN |
| `intermediate/scan-result.json` | 文件清单 + 语言 + importMap（跨增量复用） | Phase 1 SCAN | Phase 1.5 BATCH、Phase 2 增量 |
| `intermediate/batches.json` | 批次划分 + neighborMap | Phase 1.5 `compute-batches.mjs` | Phase 2 |
| `intermediate/batch-*.json` | 每批 LLM 输出 | Phase 2 file-analyzer subagent | merge 脚本 |
| `intermediate/assembled-graph.json` | 合并 + 归一化后的全图 | Phase 2 merge / Phase 6 review-only 复制 | Phase 3/6 |
| `intermediate/layers.json`, `tour.json` | Phase 4/5 subagent 输出 | architecture / tour subagent | Phase 6 归一化 |
| `intermediate/review.json` | 校验结果（issues + warnings + stats） | Phase 6 inline validator / graph-reviewer | Phase 7 final report |
| `domain-graph.json`（可选） | 业务域图，与主图同 schema | `understand-domain` skill | dashboard `DomainGraphView` |
| `diff-overlay.json`（可选） | `{changedNodeIds, affectedNodeIds}` 增量高亮 | `understand-diff` skill | dashboard `DiffToggle` |

> 关键：dashboard 真正「直接消费」的只有 `knowledge-graph.json`、`meta.json`、`config.json`、`domain-graph.json`、`diff-overlay.json`。`fingerprints.json` / `intermediate/*` 只是后台流水线状态，前端不读。

---

## 1. `knowledge-graph.json` 字段结构

Zod schema 在 `packages/core/src/schema.ts:421-429`，TS 类型在 `packages/core/src/types.ts:91-99`。

```ts
KnowledgeGraph = {
  version: string,                       // "1.0.0"
  kind?: "codebase" | "knowledge",       // 前端据此切到 knowledge 视图
  project: ProjectMeta,
  nodes: GraphNode[],
  edges: GraphEdge[],
  layers: Layer[],
  tour:   TourStep[],
}
```

### 1.1 `ProjectMeta`（必填，缺失 → fatal）

```ts
{
  name: string,
  languages: string[],          // 小写 id（"typescript"、"python"、"dockerfile"…），字典序
  frameworks: string[],         // 可为空数组
  description: string,
  analyzedAt: string,           // ISO 8601
  gitCommitHash: string,        // 40 字符 SHA
}
```

### 1.2 `GraphNode`

```ts
{
  id: string,                                              // 见下面 ID 约定
  type:  // 21 种（5 code + 5 non-code-file + 3 infra/schema + 3 domain + 5 knowledge）
    | "file" | "function" | "class" | "module" | "concept"
    | "config" | "document" | "service" | "table" | "endpoint"
    | "pipeline" | "schema" | "resource"
    | "domain" | "flow" | "step"
    | "article" | "entity" | "topic" | "claim" | "source",
  name: string,
  filePath?: string,            // 相对项目根；持久化前会被 sanitize 成相对路径
  lineRange?: [number, number], // 闭区间
  summary: string,              // 必填，缺失则 schema 用 name 兜底
  tags: string[],               // 必填，缺失则 schema 兜底 []
  complexity: "simple" | "moderate" | "complex",
  languageNotes?: string,
  domainMeta?: DomainMeta,      // 仅 domain/flow/step 用
  knowledgeMeta?: KnowledgeMeta // 仅 article/entity/topic/claim/source 用
}
```

**ID 命名约定（强约定，前端 / 后续 merge 依赖）：**

| type | ID 形式 |
|---|---|
| `file` / `config` / `document` / `service` / `pipeline` / `schema` / `resource` | `<type>:<relative-path>` |
| `function` / `class` | `<type>:<relative-path>:<name>` |
| `table` | `table:<relative-path>:<table-name>` |
| `endpoint` | `endpoint:<relative-path>:<endpoint-name>` |
| `module` / `concept` / `domain` / `article` / `entity` / `topic` / `claim` / `source` | `<type>:<slug>` |
| `flow` | `flow:<slug>` |
| `step` | `step:<flowSlug>:<relative-path>:<stepSlug>`（flowSlug 来自 `flow_step` 边反查） |

`packages/core/src/analyzer/normalize-graph.ts` 提供幂等的 `normalizeNodeId()`：剥多余前缀、补缺前缀、处理 `step` 的 flow 区分。

### 1.3 `GraphEdge`

```ts
{
  source: string,    // 必须是 nodes 中已有的 id（否则 Tier 3 丢弃）
  target: string,    // 同上
  type: /* 35 种 */,
  direction: "forward" | "backward" | "bidirectional",
  description?: string,
  weight: number,    // [0, 1]，超出会被 clamp
}
```

**35 种 EdgeType（按类别）：**

| 类别 | 类型 |
|---|---|
| Structural | `imports`, `exports`, `contains`, `inherits`, `implements` |
| Behavioral | `calls`, `subscribes`, `publishes`, `middleware` |
| Data flow | `reads_from`, `writes_to`, `transforms`, `validates` |
| Dependencies | `depends_on`, `tested_by`, `configures` |
| Semantic | `related`, `similar_to` |
| Infrastructure | `deploys`, `serves`, `provisions`, `triggers` |
| Schema / Data | `migrates`, `documents`, `routes`, `defines_schema` |
| Domain | `contains_flow`, `flow_step`, `cross_domain` |
| Knowledge | `cites`, `contradicts`, `builds_on`, `exemplifies`, `categorized_under`, `authored_by` |

**权重约定（dashboard 不强依赖，但同语义应保持）：** `contains`=1.0、`inherits/implements`=0.9、`calls/exports/defines_schema`=0.8、`imports/deploys/migrates`=0.7、`depends_on/configures/triggers`=0.6、其余默认 0.5。

### 1.4 `Layer`

```ts
{ id: string, name: string, description: string, nodeIds: string[] }
```

四个字段都必填；`nodeIds` 会被 schema 过滤掉指向不存在 node 的项。

### 1.5 `TourStep`

```ts
{
  order: number,             // 1-based，需 sort
  title: string,
  description: string,
  nodeIds: string[],
  languageLesson?: string,   // 可选
}
```

### 1.6 可选 metadata

```ts
DomainMeta = {
  entities?: string[],
  businessRules?: string[],
  crossDomainInteractions?: string[],
  entryPoint?: string,
  entryType?: "http" | "cli" | "event" | "cron" | "manual",
}

KnowledgeMeta = {
  wikilinks?: string[],
  backlinks?: string[],
  category?: string,
  content?: string,
}
```

---

## 2. 校验流水线（必须复刻）

`validateGraph()` 是 dashboard 加载时和持久化前都跑的 4 层流水线（`schema.ts:499-663`）。任何复刻实现都要按此顺序：

| Tier | 名称 | 行为 | 失败后果 |
|---|---|---|---|
| 1 | `sanitizeGraph` | `null→[] / undefined`；`type/complexity/direction` 小写化；剥 `filePath/lineRange/languageNotes/description` 的 null | 不致命 |
| 2 | `normalizeGraph` + 别名表 | 把 `fn→function`、`extends→inherits`、`low→simple` 等 LLM 常见别名映射到 canonical 值（`NODE_TYPE_ALIASES` / `EDGE_TYPE_ALIASES` / `COMPLEXITY_ALIASES` / `DIRECTION_ALIASES`） | 不致命 |
| 2 | `autoFixGraph` | 补 `type`（默认 `file`）、补 `complexity`（默认 `moderate`）、补 `tags`（默认 `[]`）、补 `summary`（默认 name）、补 `direction`（默认 `forward`）、补 `weight`（默认 0.5）、字符串 weight 转数、weight clamp 到 [0,1] | 不致命，记 issue |
| 3 | 逐条校验 + 丢弃 | 对每条 node / edge / layer / tourStep 跑 Zod `safeParse`；不合法就丢弃并记 issue；edge 的 source/target 必须在 nodes 集合中；layer / tourStep 的 nodeIds 过滤掉悬挂引用 | 丢弃部分数据 |
| 4 | fatal | 顶层不是对象；`nodes/edges/layers/tour` 存在但非数组；`project` 不符 ProjectMetaSchema；最终 `validNodes.length === 0` | 整张图判失败 |

返回值：`{ success, data?, issues: GraphIssue[], fatal?, errors? }`，`issues` 区分 `auto-corrected / dropped / fatal`。前端把 `dropped` 打 `console.error`、`auto-corrected` 打 `console.warn`。

> 强不变量：edge 的 source/target 必须 hit 一个 valid node id；layer / tour nodeIds 在最终返回时已被过滤为「确实存在的节点」。

---

## 3. `meta.json` 结构（`AnalysisMeta`，types.ts:108-114）

```ts
{
  lastAnalyzedAt: string,         // ISO 8601
  gitCommitHash: string,          // 40 字符 SHA
  version: string,                // 与 knowledge-graph.version 一致 ("1.0.0")
  analyzedFiles: number,          // 已分析文件数
  theme?: ThemeConfig = { presetId: string, accentId: string }  // 可选，dashboard 用于主题
}
```

写入顺序：**先写 fingerprints.json，再写 meta.json**（issue #152：反过来会让下次运行看到「新 commit + 空 fingerprint」，触发全量 STRUCTURAL → FULL_UPDATE 死循环）。

dashboard 读 `meta.json` 主要是为了 `theme`；`gitCommitHash` 在 dashboard 里不直接用（staleness 是后台流水线算的）。

---

## 4. `fingerprints.json` 结构与增量分级

`packages/core/src/fingerprint.ts`。整文件定义「结构指纹」与三档变化分级，是增量更新的核心。

### 4.1 Schema

```ts
FingerprintStore = {
  version: "1.0.0",
  gitCommitHash: string,
  generatedAt: string,             // ISO 8601
  files: Record<filePath, FileFingerprint>,
}

FileFingerprint = {
  filePath: string,
  contentHash: string,             // sha256(fileContent) hex
  functions: FunctionFingerprint[], // name, params[], returnType?, exported, lineCount
  classes: ClassFingerprint[],      // name, methods[], properties[], exported, lineCount
  imports: ImportFingerprint[],     // source, specifiers[]
  exports: string[],
  totalLines: number,
  hasStructuralAnalysis: boolean,   // false 表示只算了 contentHash（无 tree-sitter 支持）
}
```

### 4.2 三档变化分级（`compareFingerprints`）

| ChangeLevel | 触发条件 | 后果 |
|---|---|---|
| `NONE` | `contentHash` 完全一致 | 跳过该文件 |
| `COSMETIC` | 内容变了但所有结构签名（函数名 / params / returnType / exported / 行数 ±50% / class methods / properties / imports / exports）都未变 | 不重算图，记 cosmetic-only |
| `STRUCTURAL` | 任一签名变化；新增 / 删除文件；任一方 `hasStructuralAnalysis=false`（保守） | 该文件重算图 |

### 4.3 更新动作分级（`change-classifier.ts`）

输入 `ChangeAnalysis + totalFilesInGraph + allKnownFiles`，输出 `UpdateDecision`：

| action | 触发 | rerunArchitecture | rerunTour |
|---|---|---|---|
| `SKIP` | 全部 NONE/COSMETIC | false | false |
| `PARTIAL_UPDATE` | 局部 STRUCTURAL，目录树未变，≤10 个结构变更 | false | false |
| `ARCHITECTURE_UPDATE` | 顶层目录变化，或 >10 个结构变更 | true | true |
| `FULL_UPDATE` | >30 个结构变更，或 >50% 文件结构变更 | true | true |

`filesToReanalyze` 始终是 `[...structurallyChangedFiles, ...newFiles]`。

### 4.4 合并语义（`staleness.mergeGraphUpdate`）

```
1. removedNodeIds = { nodes whose filePath ∈ changedFilePaths }
2. retainedNodes  = existingNodes \ removedNodeIds
3. retainedEdges  = existingEdges where source ∉ removedNodeIds && target ∉ removedNodeIds
4. result = { ...existing, project: { ...project, gitCommitHash, analyzedAt: now }, nodes: [...retained, ...new], edges: [...retained, ...new] }
```

**关键不变量：** 边的 source/target 在合并后必须仍能 hit 一个 retained 或 new 的 node id。任何引用被删 node 的旧边都会被丢弃（不只是 source 端，target 端也会）。

---

## 5. `config.json` 结构（`ProjectConfig`）

```ts
{
  autoUpdate: boolean,          // 是否在 commit hook 上自动跑增量
  outputLanguage?: string,      // "zh" | "en" | "ja" …；summaries/tags/tour 用什么语言
}
```

dashboard 只读 `outputLanguage`（用于 I18n 选择）；`autoUpdate` 由 git hook / 后台流水线消费。

---

## 6. 流水线：full vs incremental

`skills/understand/SKILL.md` 定义的 7 阶段流程。所有「subagent dispatch」对应到 LLM 调用，「bundled script」对应到确定性 Go 可复刻的逻辑。

### 6.1 Phase 0 — Pre-flight（决策）

输入：
- `$ARGUMENTS`：`--full`、`--auto-update`、`--no-auto-update`、`--review`、`--language <code>`、可选目录路径
- 现有 `.agentteam-insight/{knowledge-graph.json, meta.json}`
- `git rev-parse HEAD`

决策表：

| 条件 | 动作 |
|---|---|
| `--full` 或无现有图 / meta | **full** 走 Phase 1+ |
| `--review` + 现有图 + commit 相同 | 跳到 Phase 6（只重审） |
| 现有图 + commit 相同 | 问用户：full / review / 不做 |
| 现有图 + commit 不同 | **incremental**：`git diff <lastHash>..HEAD --name-only` |

**worktree 重定向**：如果在 git worktree 里，输出写到 main checkout 根（避免 worktree 被销毁时丢图）。

### 6.2 Phase 0.5 — Ignore 配置

- 若 `.agentteam-insightignore` 不存在，跑 `generate-ignore.mjs` 基于 `.gitignore` 生成建议；**等用户确认**。
- 已存在就直接进入下一阶段。

### 6.3 Phase 1 — SCAN（仅 full）

- 输入：`$PROJECT_ROOT`、`README` 前 3000 字、package manifest、目录树
- 执行：`project-scanner` subagent（LLM）
- 输出：`intermediate/scan-result.json` = `{ projectName, projectDescription, languages[], frameworks[], files[{path, sizeLines, fileCategory, language}], complexity, importMap, filteredByIgnore }`
- `fileCategory` 取值：`code | config | docs | infra | data | script | markup`
- 增量时跳过此阶段，复用上次 `scan-result.json`（issue #293）

### 6.4 Phase 1.5 — BATCH（确定性）

`node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOT`：
- 输入：`scan-result.json`（或增量时 `--changed-files=<file>`）
- 输出：`intermediate/batches.json` = `{ batches: [{ batchIndex, batchFiles, batchImportData, neighborMap }] }`
- 语义：按文件大小、语言、import 邻接分批；每批带「跨批邻居」信息以便发跨批边

### 6.5 Phase 2 — ANALYZE（LLM 重头）

- 输入：每批 `batches[i]`
- 执行：`file-analyzer` subagent，**最多 5 并发**
- 输出：每批一个 `intermediate/batch-<batchIndex>.json`（或 `batch-<i>-part-<k>.json` 分片）= `{ nodes[], edges[] }`
- 后处理：`python <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOT`
  - 跑 `normalizeBatchOutput`：node ID 规整、complexity 规整、edge source/target 重写、按 `(source,target,type)` 去重、丢悬挂边
  - 跑 `tested_by` linker：两遍规整测试覆盖边
- 输出：`intermediate/assembled-graph.json`

**Incremental 增量路径：**
1. 写 `changed-files.txt`
2. `compute-batches.mjs --changed-files=...` 出增量 batches.json
3. 跑同样的 file-analyzer
4. 把现有图的 nodes/edges（已 prune 掉 changed filePath 的）写成 `batch-existing.json`
5. 跑同一个 merge 脚本，合并 `batch-existing.json` 与新 `batch-*.json`

### 6.6 Phase 3 — ASSEMBLE REVIEW（LLM）

- 输入：assembled graph + 所有 batch 文件 + import map + merge 脚本 stderr
- 执行：`assemble-reviewer` subagent
- 输出：`intermediate/assemble-review.json`（仅 warnings / 修正建议）

### 6.7 Phase 4 — ARCHITECTURE（LLM）

- 输入：所有 file-level nodes（不含 function/class）、`imports` 边、所有边、目录树、语言 / 框架上下文文件、（增量时）上一轮 layers
- 执行：`architecture-analyzer` subagent
- 输出：`intermediate/layers.json`
- 后处理（确定性）：剥 envelope、`nodes → nodeIds`、补 `layer:<slug>` id、文件路径转 `file:<rel>` 前缀、丢悬挂

### 6.8 Phase 5 — TOUR（LLM）

- 输入：file-level nodes、layers（不含 nodeIds）、所有边、README、entry point
- 执行：`tour-builder` subagent
- 输出：`intermediate/tour.json`
- 后处理（确定性）：剥 envelope、`nodesToInspect → nodeIds`、`whyItMatters → description`、路径转前缀、丢悬挂、按 `order` 排序

### 6.9 Phase 6 — REVIEW（确定性默认 + 可选 LLM）

- 装配 `{ version, project, nodes, edges, layers, tour }` 写到 `intermediate/assembled-graph.json`
- 默认路径：跑一段内联 Node 验证脚本（`ua-inline-validate.cjs`），检查：
  - 所有 node 有 `id/type/name/summary/tags`
  - edge source/target 在 nodeIds 中
  - 每个 file-level node 至少属于一个 layer（否则 issue）
  - 每个 node 不在多个 layer 中
  - tour nodeIds 都存在
  - 无 orphan node 警告
- `--review` 路径：跑 `graph-reviewer` subagent + 跑 `validateGraph()`（schema.ts 4 层）
- 输出：`intermediate/review.json = { issues[], warnings[], stats }`

### 6.10 Phase 7 — SAVE（确定性）

1. 写 `.agentteam-insight/knowledge-graph.json`（持久化前对每条 node 的 `filePath` 做 sanitize：绝对路径转相对、外的绝对路径仅保留 basename）
2. 写 `intermediate/fingerprint-input.json` → 跑 `build-fingerprints.mjs` → 写 `fingerprints.json`
3. **仅当 step 2 成功**，写 `meta.json`
4. 把 `intermediate/` 里除 `scan-result.json` 之外的文件全 `mv` 到 `.trash-<ts>/`（避免 destructive-action gate；7 天后由 Phase 0 清理）
5. 报告 stats；若校验未通过则不自动启 dashboard

### 6.11 流水线不变量（必须保持）

1. **持久化前 sanitize filePath**：绝对路径不能写入 `knowledge-graph.json`（防泄露开发者目录布局）
2. **fingerprints 先于 meta**：否则增量分级永远 STRUCTURAL → FULL_UPDATE
3. **edge 端点引用完整性**：合并 / 持久化前必须保证 source/target 都存在
4. **layer / tour nodeIds 过滤**：写入前过滤掉悬挂引用
5. **scan-result.json 跨增量保留**：否则每次增量都要重跑 Phase 1（~157k tokens / 158s）
6. **batch 文件名严格 `batch-<batchIndex>.json` 或 `batch-<batchIndex>-part-<k>.json`**：merge 脚本 regex 静默丢弃其他命名
7. **worktree 重定向**：worktree 里的 `.agentteam-insight/` 会在 session 结束被销毁，必须重定向到 main checkout 根

---

## 7. 前端（dashboard）直接消费约束

来自 `packages/dashboard/src/App.tsx:50-200`：

1. **HTTP 路径**：
   - `/knowledge-graph.json?token=<sessionToken>`（生产）或 `import.meta.env.VITE_GRAPH_URL`（demo）
   - `/meta.json?token=...`、`/config.json?token=...`、`/diff-overlay.json?token=...`、`/domain-graph.json?token=...`
   - token 来自 URL `?token=` → 写 sessionStorage → 之后从 sessionStorage 读
2. **加载即校验**：拿到 graph 立即 `validateGraph(data)`，**失败则整图不显示**（`setLoadError("Invalid knowledge graph: ...")`），成功才 `setGraph(result.data)` 并把 issues 打 console
3. **kind 字段**：`kind === "knowledge"` 时切到 knowledge 视图（`setViewMode("knowledge")` + `setIsKnowledgeGraph(true)`）
4. **diff-overlay 形状契约**：必须是 `{ changedNodeIds: string[], affectedNodeIds: string[] }`，且 `changedNodeIds.length > 0` 才生效
5. **meta.theme**：若有则切主题；`config.outputLanguage` 切 I18n
6. **节点 ID 必须稳定**：dashboard 用 id 做 React key、引用查找、edge 端点匹配；任何复刻实现的 id 生成规则都不能换
7. **可选内容**：`domain-graph.json` 404 静默；`diff-overlay.json` 404 静默

> 这意味着 AgentTeam Insight 的 Go 实现只要能写出「过 `validateGraph` 的 `knowledge-graph.json` + 一个 `meta.json`」，前端 dashboard 就能直接用。`fingerprints.json` / `config.json` / `intermediate/*` 不影响首屏加载。

---

## 8. 必须「确定性复刻」 vs 可「LLM/agent 增强」分类

### 8.1 必须确定性复刻（不做就走不到前端）

> 这些是 AgentTeam Insight `graph` 子命令的「最小可工作子集」。即便先不做 LLM 部分，也要保证这部分能产出 dashboard 可消费的 JSON。

| # | 模块 | 原始位置 | 复刻要点 |
|---|---|---|---|
| D1 | **Schema / Zod → Go struct** | `schema.ts`、`types.ts` | 21 node types、35 edge types、ProjectMeta、Layer、TourStep、DomainMeta、KnowledgeMeta 全字段；保留 UA passthrough 边界（GraphNode/DomainMeta/KnowledgeMeta 额外字段不报错） |
| D2 | **4 层校验流水线** | `validateGraph` | sanitize → normalize aliases → autoFix → drop-broken → fatal；返回 `{success, data, issues[], fatal?}` |
| D3 | **别名表** | `NODE_TYPE_ALIASES`、`EDGE_TYPE_ALIASES`、`COMPLEXITY_ALIASES`、`DIRECTION_ALIASES` | 全量搬到 Go map，否则 LLM 输出走不通 |
| D4 | **Node ID 规整** | `normalizeNodeId`、`normalizeBatchOutput` | 幂等、剥重复前缀、step 的 flowSlug 反查 |
| D5 | **Edge 引用完整性** | Tier 3 + `mergeGraphUpdate` | 任何写入前 filter dangling；合并保留 retainedNodes/retainedEdges 语义 |
| D6 | **Layer / tour 后处理** | SKILL Phase 4/5 | envelope unwrap、字段 rename、文件路径转前缀、丢悬挂、按 order 排序 |
| D7 | **filePath sanitize** | `persistence.sanitiseFilePaths` | 绝对路径 → 相对项目根；外部绝对路径 → basename |
| D8 | **持久化文件布局** | `persistence/index.ts` | `.agentteam-insight/{knowledge-graph.json, meta.json, fingerprints.json, config.json}` + `.agentteam-insightignore` |
| D9 | **写入顺序：fingerprints 先于 meta** | issue #152 | 强约束 |
| D10 | **Fingerprint schema** | `FileFingerprint` / `FingerprintStore` | contentHash sha256 hex、结构签名字段、`hasStructuralAnalysis` 标志 |
| D11 | **三档变化分级** | `compareFingerprints` | NONE / COSMETIC / STRUCTURAL；签名比对规则要完全一致（含 lineCount ±50% 阈值） |
| D12 | **更新动作分级** | `classifyUpdate` | SKIP / PARTIAL / ARCHITECTURE / FULL 四档阈值（30 文件 / 50% / 顶层目录变化 / 10 文件） |
| D13 | **scan-result.json 持久化与复用** | issue #293 | 跨增量保留；`fileCategory` 七值枚举稳定 |
| D14 | **worktree 重定向** | Phase 0 | `git rev-parse --git-dir` vs `--git-common-dir` 不一致时重定向 |
| D15 | **.agentteam-insightignore 解析** | `ignore-filter.ts` | gitignore 风格；与 `.gitignore` 合并 |
| D16 | **batch 文件命名约束** | SKILL Phase 2 | 严格 `batch-<i>.json` / `batch-<i>-part-<k>.json`（merge regex 依赖） |
| D17 | **diff-overlay schema** | App.tsx:174-187 | `{changedNodeIds: string[], affectedNodeIds: string[]}`；空数组不生效 |

### 8.2 后续可由 LLM/agent 增强（先跑通确定性骨架，再叠加）

| # | 模块 | 原始位置 | 增强空间 |
|---|---|---|---|
| L1 | **文件级 LLM 分析**（生成 summary / tags / complexity / languageNotes） | `file-analyzer` subagent | AgentTeam Insight 可接 GLM / Claude；输出仍是 GraphNode / GraphEdge，但 summary 类字段由 LLM 填 |
| L2 | **跨文件 import 解析** | Phase 1 importMap | LLM 辅助或纯 tree-sitter 都可；最终影响 batch 构造与跨批边 |
| L3 | **project-scanner**（projectName / description / frameworks） | Phase 1 | LLM 增强，可降级到从 package.json / README 启发式提取 |
| L4 | **architecture-analyzer**（layers） | Phase 4 | LLM 增强；降级方案：按目录结构 + import 边启发式分 layer（`layer-detector.ts` 已有部分启发式） |
| L5 | **tour-builder** | Phase 5 | LLM 增强；降级：`tour-generator.ts:generateHeuristicTour` 提供纯启发式 |
| L6 | **graph-reviewer** | Phase 6 `--review` | 完全可选；默认路径是确定性 inline validator |
| L7 | **assemble-reviewer** | Phase 3 | 可选；目前只产 warnings |
| L8 | **tested_by linker 的「语义反向」步骤** | merge-batch-graphs.py Pass 1 | 可降级到 Pass 2 纯路径约定配对 |
| L9 | **language lesson** | Phase 5 `languageLesson`、`language-lesson.ts` | 可选字段，dashboard 用于 LearnPanel |
| L10 | **domain graph** | `understand-domain` skill | 整个 domain/knowledge 节点是可选的，dashboard 支持单独加载 domain-graph.json |
| L11 | **多语言 outputLanguage** | `config.json` + locales/*.md | LLM 提示工程层面，不影响 schema |
| L12 | **semantic batching** | `compute-batches.mjs` | 大小+import 启发式可作为起点；语义聚类是优化 |

### 8.3 边界判断准则

> 一项能力属于 D 还是 L，看：「不这么做，dashboard 还能不能加载这张图并基本可用？」
> - 不能 → D（必须复刻）
> - 能但体验差 / 字段为空 / 缺层 / 缺 tour → L

---

## 9. 复刻到 AgentTeam Insight 的最小路径建议（送给 planner2 合成用）

按 D1–D17 顺序实现，配合 L1–L12 选择性叠加：

1. **Phase A — Schema 与校验骨架**：D1 + D2 + D3 + D17 → 能产出一张过校验的最小图
2. **Phase B — 持久化与目录布局**：D7 + D8 + D9 + D15 → 写到 `.agentteam-insight/`
3. **Phase C — Tree-sitter 静态抽取**：复刻 `extract-structure.mjs` 的 Go 版，产出 StructuralAnalysis（functions / classes / imports / exports）
4. **Phase D — 确定性 GraphBuilder**：参照 `analyzer/graph-builder.ts`，从 StructuralAnalysis 产出 `file:` / `function:` / `class:` nodes + `contains` / `imports` / `calls` edges
5. **Phase E — Fingerprint 与增量**：D10 + D11 + D12 + D13 → 支持 incremental
6. **Phase F — Layer / Tour 启发式**：D6 + L4 降级 + L5 降级 → 至少有按目录分的 layers 和 README/entry 引导的 tour
7. **Phase G — worktree 重定向与清理**：D14 + trash-dir 命名约定
8. **Phase H — LLM 增强接入点**：L1（file summary）、L3（scanner）、L4（layers）、L5（tour）按需打开
9. **Phase I — 前端联调**：本地启 Understand-Anything dashboard，把 AgentTeam Insight 产出的 `knowledge-graph.json` 喂给它，验证加载/视图/搜索

---

## 10. 已知陷阱（写实现时易踩）

1. **passthrough schema**：GraphNode 是 `.passthrough()`，额外字段不报错。Go 实现要保留未知字段（用 `map[string]any` 或类似），否则 dashboard 的一些 optional 字段（如 `domainMeta` 扩展）会被丢
2. **batch 文件命名**：regex 是 `batch-(\d+)(?:-part-(\d+))?\.json`，任何其他命名都被静默丢
3. **fingerprints.json 与 meta.json 写入顺序**：颠倒会触发 FULL_UPDATE 死循环
4. **worktree 销毁**：worktree 里的 `.agentteam-insight/` 会随 session 消失
5. **edge 双端引用**：mergeGraphUpdate 不仅删 source 端在 changed files 的边，target 端在 changed files 的边也要删
6. **`tested_by` 边方向**：production → test；反向必须翻
7. **step ID 含 flowSlug**：同文件不同 flow 的同名 step 不能合并
8. **weight clamp**：超 [0,1] 会被 clamp，不报 fatal
9. **summary 兜底**：缺 summary 的节点会用 name 兜底，不要在生成阶段就丢
10. **`type` 大小写**：sanitize 会把 `type` 小写，但 alias 表的 key 已经是小写，顺序不能反

---

报告完。等 @agentteam-planner2 据此合成实施计划。
