# card-splitter 优化方案

## 概述

card-splitter 负责将 architecture-lens 产出的架构全景图，按"一个独立单元一张卡"拆解为知识卡片（summary/）。

## 输入

| 来源 | 内容 | 路径 |
|------|------|------|
| architecture-lens 输出 | 架构全景图（含 7 层结构各层表格） | 由调度器传入 |
| architecture-lens 输出 | module-source-manifest.md（调度器逐行提取后传参） | 由调度器传入 |
| 源码目录 | 各模块的源文件 | 项目根目录 |
| 参数 | 模块名 / node_type / 源码路径 / 备注（由调度器从 manifest 提取） | 调度器传入 |

## 核心流程

```
架构全景图 → module-source-manifest.md
  │
  逐模块 dispatch card-splitter（每个模块一次 Task）
  │
  Step 1: 确认输入
  │   模块名、node_type、源码路径 必填检查
  │
  Step 2: 拆卡（一次性完成）
  │   2.1 定位源码
  │   2.2 扫描 + 快速通读
  │       ├─ 有 codegraph → codegraph_explore 取符号结构
  │       └─ 无 codegraph → find + grep 提取文件头部签名
   │   2.3 一次性判断所有子目录（默认模块=1张卡）
   │       ├─ 子目录明显不同业务 → 独立出卡
   │       ├─ 子目录在模块范围内 → 归入模块卡
   │       └─ 技术层 → 归上级
  │   2.4 汇聚
  │   2.5 对比已有卡（一次 glob *.summary.md）
  │   2.6 写 summary/
  │
  Step 3: 设 status = needs_update
  │
  校验所有 summary/ 与 architecture-overview
  │  ├─ 所有模块都有对应卡（无遗漏）
  │  └─ 每张卡指向的文件数 < 50
  │
不通过则补充/拆分 → 重校验
│
通过 → 完成拆卡
```

## 拆卡规则

### 什么是"一个独立单元"

**默认：一个模块就是一张卡。** 传入的模块已经是一个独立业务功能，其内部子目录都是该功能的组成部分。

**例外：只有当子目录的业务概念明显不属于模块的业务范围时，才独立出卡。**

```
示例：
  p_dailyexercise（每日运动）
    ├ widget/ (图表)   → 每日运动的UI组件  → ❌ 归入每日运动
    ├ behavior/ (埋点) → 每日运动的一部分   → ❌ 归入每日运动
    结果：1 张卡

  p_babyinfo（宝宝信息）
    ├ listenstory/ (听故事)  → 独立业务     → ✅ 独立出卡
    ├ watchsetting/ (手表设置) → 独立业务    → ✅ 独立出卡
    结果：3 张卡
```

核心原则：**技术实现层子包不出独立卡**。无法确认时通过快速通读辅助判断。

### 组合规则

```
仅当多个子包是同一单元的组成部分时才合并。
例如：chatlist/model + chatlist/adapter + chatlist/fragment → 合并为"聊天列表"一张卡

功能/能力/服务不同的包即使同目录也分开出卡。
例如：constants 和 event 同目录但用途不同 → 各出一张卡

合并后命名：取覆盖范围最广的包名作为卡名。
```

### 层级归属

子包组合后，node_type 以来源层为准。例如 basiclib 下的子包，统一为 `基础设施`，不拆分到不同 node_type。

## node_type 映射

| 来源层 | node_type | 拆卡粒度 |
|--------|-----------|---------|
| 第 1 层 | `项目配置` | 出 1 张总览卡 |
| 第 2 层 | `外部依赖库` | 整个表出 1 张总卡 |
| 第 3 层 | `基础设施` | 扫子包，一能力一张卡 |
| 第 4 层 | `公共服务` | 扫子包，一服务一张卡 |
| 第 5 层 | `业务模块` | 默认 1 张卡，仅超语义范围时才独立出卡 |
| 第 6 层 | `工程支撑` | 出 3 张卡：测试配置、CI/CD、外部支撑 |
| 第 7 层 | `资源文件` | 每资源目录出 1 张卡 |
| 跨层依赖 | — | 不入清单 |

## 卡片字段映射

各卡片数据来源：

| summary 字段 | 数据来源 |
|-------------|---------|
| id | `{8位hex}-{卡片英文名}`，全局唯一 |
| name | 包名/目录名中文翻译 |
| module | 模块名（如 p_health_heartrate），用于模块标识和依赖解析 |
| node_type | 按来源层映射 |
| domain | 从父模块或备注推断，不得留空 |
| code_locations | 从源码目录提取，写到具体功能子目录（见 prompt 专项说明） |
| key_classes | card-generator 回填 |
| status | 新建 = `needs_update` |
| update_scope | 新建 = `full` |

总览卡（第 1 层）字段特殊处理：

| 字段 | 来源 |
|------|------|
| name | "项目总览" |
| node_type | `项目配置` |
| code_locations | architecture-overview.md 路径 |
| key_classes | 从第 1 层各表提取关键属性（项目名、版本、SDK、模块数等）（总览卡特殊处理，其他卡由 card-generator 回填） |

## 快速通读策略

每个源文件都看，但只看头部签名（package、javadoc、class 声明、method 签名），不读方法体：

| 场景 | 方式 | 产出 |
|------|------|------|
| 有 codegraph | `codegraph_explore(query)` | 符号结构 + 类名/注释 |
| 无 codegraph | `find + grep -E "^(package\|class\|fun\|public\|...)"` | 每个文件 10-30 行签名 |

## 校验

写完全部 summary 后，做两轮检查：

**1. 全覆盖检查**：遍历 `summary/` 目录下的所有文件，逐个比对各模块在 `architecture-overview.md` 中是否有对应卡，确保第 1~7 层所有模块都有覆盖，无遗漏。

**2. 文件数检查**：每张卡的 code_locations 指向的源文件数必须 < 50。超标的卡说明粒度太粗，需进一步拆分。

不通过则补充遗漏卡 / 拆分超标卡 → 重校验 → 通过后继续。



## 完成标准

- 架构全景图的第3~5层各表正确读取
- 第3层基础设施扫子包，一能力一张卡
- 第4层公共服务扫子包，一服务一张卡
- 第5层业务模块扫子包，一功能一张卡
- 第2层外部依赖库只出 1 张总卡
- 同一单元的多个子包已正确合并
- 不同单元的包各自独立出卡，不强行合并
- summary frontmatter 所有必填字段非空
- 校验通过：architecture-overview 所有模块都有对应卡
