dev-manager-tauri/.blueprint/CONVENTIONS.md
lanrtop 6162533c77 feat: 飞轮阶段一 + 项目个人备忘功能
飞轮智能成长阶段一:
- 梦核 prompt 注入跨项目复盘笔记,新增【高频陷阱】分析节
- 修复 collect_flywheel_notes 查错表 bug(projects → project_workspaces)
- MCP append_project_note 描述内嵌【复盘】结构化格式,引导 Claude 写标准笔记
- FlywheelStats 新增 project_notes 字段,支持 win_path/wsl_path 两种路径匹配

项目个人备忘(user_project_todos):
- 新表 + 4 个 Rust command,依赖注入写法,6 个单元测试全绿
- UserTodoPanel 组件,📝 按钮展开,支持新增/勾选/删除

CONVENTIONS v1.4.4:新增 Rust 可测试性规范(依赖注入强制要求)
2026-04-11 17:21:47 +09:00

562 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
version: 1.4.4
updated: 2026-04-11
source: dev-manager-tauri
---
# 蓝图更新规则
> ⚠️ 本文件由 dev-manager-tauri 统一管理,请勿手动修改。规则更新请在 dev-manager-tauri 项目中进行。
本文件定义了 `.blueprint/` 目录的维护规范,供 Claude 在对话中遵循。
## 目录结构
```
.blueprint/
├── manifest.yaml ← 模块列表、关系、布局坐标
├── CONVENTIONS.md ← 本文件Claude 行为规则)
└── modules/
└── <module-id>.md ← 每个模块的详情 + 任务卡
```
## 何时更新蓝图
| 事件 | 操作 |
|------|------|
| 用户确认了新需求 | 在 manifest.yaml 新增模块,创建 modules/<id>.md |
| **开始执行任务卡信息1** | **任务卡前缀改为 🔵status → in_progress第一行代码之前** |
| **完成了功能实现信息2** | **任务卡前缀改为 ✅status → done验收通过之后** |
| 拆分了大需求为子任务 | 在 modules/<id>.md 中添加任务卡 |
| 调整了架构方向 | 更新 edges 关系,可能调整 area |
| 放弃了某个方向 | 删除对应模块和文件,或标记 status: abandoned |
| **重构已完成的模块(迭代)** | **manifest.iteration 递增,旧任务卡保留,新建替代任务卡,模块加 `## 迭代记录`** |
## 模块状态定义
| 状态 | 含义 | 色标 |
|------|------|------|
| `done` | 已实现并验证 | 🟢 绿色 |
| `in_progress` | 正在开发 | 🔵 蓝色 |
| `planned` | 已规划,需求明确 | 🟡 黄色 |
| `concept` | 构思中,信息不完整 | ⚪ 灰色 |
| `blocked` | 执行受阻,需架构师介入 | 🔴 红色 |
## 任务卡格式
modules/<id>.md 中的每个二级标题(`###`)是一张任务卡:
```markdown
### 📋 任务标题
- status: todo
- complexity: M
- files: src/path/to/file.tsx, src-tauri/src/commands/xxx.rs
- depends: other-module-id
- acceptance: 简要描述完成标准
- blocked_reason: (仅 status: blocked 时填写)执行受阻的具体原因
- iteration: 1 # 可选,省略时继承 manifest 顶层 iteration 值
- replaces: 旧任务标题 # 可选,仅重构任务填写,说明本卡替代哪张旧卡
详细说明(可选)。
```
> **files 回填规则**:任务卡的 `files` 在规划时填入预期路径。实现完成后,若实际文件与规划有出入(合并、拆分、重命名),须在标记 ✅ done 之前将 `files` 更新为实际路径——这保证下次续接或派发时上下文准确。
### 状态前缀
| 前缀 | status | 含义 |
|------|--------|------|
| ✅ | done | 已完成信息1 + 信息2 均已写入) |
| 🔵 | in_progress | 执行中或被中断只有信息1等待信息2 |
| 📋 | todo | 可派发(信息充足) |
| 💭 | concept | 构思中 |
| 🔴 | blocked | 执行受阻 |
### 可派发标准(📋)
任务卡同时满足以下条件时标记为 📋:
1. status 为 `todo`
2. 有明确的 `files`(涉及哪些文件)
3. 有明确的 `acceptance`(验收标准)
4. complexity 为 `S``M`
不满足条件的 todo 任务保持无前缀,说明还需补充信息。
## manifest.yaml 规范
### 顶层字段
```yaml
version: 1
name: 项目名称
iteration: 1 # 当前迭代号(默认 1重构已完成模块时手动递增
updated: YYYY-MM-DD
```
### 模块字段
```yaml
- id: kebab-case-id # 唯一标识,同时是 modules/ 下的文件名
name: 显示名称
area: frontend # 所属领域 ID
status: done # done / in_progress / planned / concept
progress: 100 # 0~100done 自动视为 100
position: [x, y] # React Flow 画布坐标(可选)
```
### 边字段
```yaml
- from: module-a
to: module-b
type: dependency # dependency实线/ related虚线
```
### 领域字段
```yaml
- id: frontend
name: 前端
color: "#3B82F6" # 节点边框/分组背景色
```
## 进度计算
模块的 progress 由其任务卡的完成比例推算:
- 无任务卡的模块:手动设置 progress
- 有任务卡的模块(单迭代):`完成数 / 总数 × 100`(取整)
- 有任务卡的模块(多迭代):**只计当前 iteration 的任务卡**,旧迭代 done 任务不计入分母,避免历史完成率虚高
## 迭代管理
### 何时递增 iteration
**需要递增**(重构已完成的功能):
- 一个已有 ✅ done 任务卡的模块需要重新设计或重写
- 核心交互/架构方案被替换(不是在原有基础上扩展)
**不需要递增**(正常演进):
- 在现有模块上添加新功能
- 修复 bug
- 新增模块
### 重构时的操作步骤
1. **manifest.yaml 顶层** `iteration` +1更新 `updated` 日期
2. **旧任务卡**:保留原位,在卡片末尾加一行注释说明被替代(不改 status、不删除
```markdown
### ✅ 旧功能实现
- status: done
- iteration: 1
...
> ⚠️ 已被迭代 2「新功能实现」替代仅作历史记录。
```
3. **新任务卡**:新建替代卡,标注 `iteration``replaces`
```markdown
### 📋 新功能实现
- status: todo
- iteration: 2
- replaces: 旧功能实现
- complexity: M
- files: ...
- acceptance: ...
```
4. **模块文件**:追加 `## 迭代记录` 段落(见下方格式)
5. **模块 status**:重置为 `in_progress`progress 按当前迭代任务卡重新计算
### 迭代记录格式
模块文件末尾追加,每次迭代一条:
```markdown
## 迭代记录
### 迭代 2YYYY-MM-DD
- **原因**React Flow 在 50+ 节点时性能卡顿,重构为虚拟化渲染方案
- **替代**「React Flow 节点图渲染迭代1」→「节点图虚拟化渲染」
- **保留**迭代1的任务卡作为历史记录不删除
```
### 梦核如何感知迭代
- usage.json 快照携带 `iteration` 字段(见飞轮外环章节)
- 飞轮数据层按 iteration 分组计算完成率,迭代跳变(如 1→2 后完成率骤降)是正常信号
- 梦核 prompt 会展示「完成率跳变项目」摘要Opus 据此判断是主动重构还是项目失控
## 决策记录格式
模块文件中可添加 `## 决策记录` 段落,记录架构决策的 WHY
```markdown
## 决策记录
- 选择阿里云 OSS 而非 GitHub Releases 作为更新源,因为国内用户访问 GitHub 不稳定
- 使用 document-driven 蓝图而非数据库存储,因为 Claude 天然支持文件读写且可版本控制
```
桌面 App 会以琥珀色卡片展示在模块详情面板中。
## 飞轮工作流
Claude 在每次对话中应遵循以下流程:
### 阶段 1进入对话 — 读取蓝图
```
读取 .blueprint/manifest.yaml → 了解项目全景和当前状态
```
- 识别哪些模块 in_progress哪些有可派发任务
- 用简短话语告诉用户当前项目进展(如 "18/22 模块已完成2 个进行中"
### 阶段 2讨论需求 — 同步更新蓝图
| 用户说的 | Claude 应做的 |
|----------|--------------|
| "我想加个 XX 功能" | 确认需求后manifest 加模块 (status: concept),创建 modules/<id>.md |
| "XX 功能需要 A、B、C 三步" | 在模块文件中拆分为 3 张任务卡 |
| "这个方案确定了" | 模块 status → planned补充任务卡的 files/acceptance |
| "开始做 XX" | 模块 status → in_progress |
| "为什么选这个方案" | 记录到模块的 `## 决策记录` |
### 阶段 3实现功能 — 两阶段执行锁
任务卡执行遵循**两阶段提交**开始写第一行代码前加锁信息1全部完成后解锁信息2。只有信息1没有信息2说明任务被中断。
```
📋 todo
↓ 【信息1加锁】第一行代码改动之前先更新任务卡
🔵 in_progress
↓ 执行代码改动
├─ 成功 → 【信息2解锁】更新任务卡前缀为 ✅ → status: done
│ 所有任务卡完成 → 模块 status: done
│ 更新 manifest.yaml 的 updated 日期
├─ 失败且可自行解决 → 继续尝试,保持 🔵
└─ 失败且无法独立解决 → 🔴 blocked + blocked_reason → 留给架构师处理
```
#### 锁悬空时的续接规则
新会话读蓝图发现 🔵 任务卡只有信息1没有信息2
1. 检查任务卡的 `files` 字段,读取相关文件的**当前代码状态**
2. 判断已完成什么、还缺什么
3. 从断点继续执行,**不重头来过**
4. 完成后补写 ✅信息2关锁
#### 架构师处理 blocked 任务
当 Opus 读蓝图发现 🔴 blocked 任务时:
1. 分析 `blocked_reason`,定位根因
2. 根据情况采取行动:
- **拆**:任务本身拆得不好 → 标记原任务 abandoned → 产出新任务卡
- **补**:缺少前置依赖 → 创建前置任务卡 → 原任务加 depends
- **调**:架构方向需调整 → 更新模块设计/决策记录 → 重新拆任务卡
3. 新任务卡就绪后,原 blocked 任务可标记为 abandoned 或更新为 todo
### 阶段 4回顾 — 用户在桌面 App 查看
用户打开蓝图可视化:
- 看到全景进度
- 在「下一步」面板查看可派发任务
- 点击「复制派发」→ 粘贴给新 Claude 会话
### 飞轮的反馈信号
Claude 应关注这些信号来决定是否更新蓝图:
- 用户说"这个做完了" → 标记 done
- 用户说"这个不做了" → 标记 abandoned 或删除
- 用户说"这个要拆一下" → 拆分任务卡
- 用户说"记住这个决策" → 写入决策记录
- 用户没提蓝图但在讨论功能 → 主动提议更新蓝图
- 执行模型遇到无法解决的问题 → 标记 🔴 blocked + blocked_reason
- Opus 看到 blocked 任务 → 分析原因,拆/补/调后产出新任务卡
## 交互质量复盘笔记
> 本章节仅适用于**开发执行阶段**(实现代码时),初始化蓝图或同步蓝图时无需执行。
每完成一张 **M 或 L 复杂度**的任务卡后Claude 应通过 `append_project_note`MCP 工具写入一条结构化复盘笔记。这是飞轮外环的原始数据来源——积累足够多条后Claude 读一遍即可识别高频坑点和高效模式。
### 笔记格式
```
【复盘】<任务标题>
任务类型: 后端命令 | 前端组件 | 数据层 | MCP工具 | 架构调整
复杂度: S | M | L
实际轮数: N轮预估 M 轮)
是否返工: 是/否
返工原因: <若返工,一句话说明根因,如"WSL路径格式未预料">
有效策略: <本次奏效的做法,一句话>
遗留风险: <若有,一句话>
```
### 触发时机
| 情况 | 操作 |
|------|------|
| M/L 任务卡标记为 ✅ done | 写一条复盘笔记source: mcp |
| 遇到非预期的系统边界或平台差异 | 额外写一条"陷阱记录",遗留风险填具体复现条件 |
| 同类问题第二次出现 | 在新笔记中引用旧笔记时间戳,说明"同类问题复发" |
### 示例
```
【复盘】WSL项目路径检测
任务类型: 后端命令
复杂度: M
实际轮数: 6轮预估 2 轮)
是否返工: 是
返工原因: wsl_path 字段存的是 Windows UNC 格式(\\wsl.localhost\Ubuntu-22.04\...),而非 Linux 路径,导致 wsl 子命令收到错误参数
有效策略: 在函数入口统一用 unc_to_distro_and_linux() 转换,一次解析,全程复用
遗留风险: Path::exists() 对 \\wsl.localhost\ 路径无效,所有 WSL 路径存在性检查必须走 wsl -d distro -- test -d/f
```
> 这些笔记不替代决策记录(决策记录记架构 WHY复盘笔记记执行 HOW 和踩坑)。两者都写。
---
## 飞轮外环(跨项目数据反哺)
内环描述的是单项目的执行流程。外环负责**把多个项目的执行数据聚合起来,反哺 CONVENTIONS 本身**。
### 数据采集usage.json 快照
每次 Claude 读取 `.blueprint/manifest.yaml`(通过炼境打开蓝图时触发),炼境自动向项目的 `.blueprint/usage.json` 追加一条当日快照:
```json
{
"project_name": "my-project",
"snapshots": [
{
"at": "2026-04-05T10:00:00Z",
"conventions_version": "1.3.0",
"iteration": 1,
"modules": { "total": 22, "done": 18, "in_progress": 2, "planned": 1, "concept": 1, "blocked": 0, "stalled": [] },
"tasks": { "total": 45, "done": 38, "in_progress": 3, "blocked": 0, "todo": 4, "dispatched": 3 },
"blocked_reasons": []
}
]
}
```
- `dispatched` = 当前满足📋可派发条件的任务数(有 files + acceptance + complexity S/M
- 节流:同一 UTC 日期只写一条,避免多次打开导致数据膨胀
- 文件存于项目本地,不上传服务器
### 数据聚合:飞轮指标
炼境跨项目读取所有 usage.json`conventions_version` 分组,对每个版本队列计算 4 项指标:
| 指标 | 计算方式 | 含义 |
|------|----------|------|
| 可派发率 | dispatched / total tasks | 任务卡规划质量,越高说明任务定义越清晰 |
| 任务完成率 | done / total tasks | 整体执行进度均值 |
| 飞轮持续率 | 30天内有快照的项目比例 | 外环是否在持续运转 |
| 阻塞密度 | blocked / total tasks | 系统性阻塞程度,越低越好 |
迭代感知:同一项目若 `iteration` 值增大,说明发生了主动重构。飞轮数据层对此类项目单独计算「当前迭代完成率」,与历史累计值区分展示,避免虚高。梦核 prompt 中标注「完成率跳变项目」供 Opus 分析是健康重构还是项目失控。
### 梦核分析:记忆巩固
炼境将聚合数据 + 当前 CONVENTIONS 全文格式化为提示词,发给 **Claude Opus** 做两件事:
- **固化**:哪些跨项目重复出现的阻塞模式、哪些有效实践,值得写进 CONVENTIONS
- **遗忘**哪些休眠项目90天无快照的 AI 文档可以归档,哪些 CONVENTIONS 规则已过时
### 闭环路径
```
执行任务 → usage.json 快照
炼境聚合指标
梦核提示词 → Claude Opus 分析
用户/维护者审核建议
更新 CONVENTIONS.md 源码 + 版本号
发布新版炼境
治理面板 sync_blueprint_rules → 分发到各项目
```
> **注意**CONVENTIONS.md 编译进炼境二进制,更新需要修改炼境源码并发版,不支持运行时修改。这是有意为之的设计——保证规则版本可追溯,防止随意污染。
## 复杂功能分析流程(/architect
当需求复杂度为 **L 级**时,在写任何代码之前执行本流程。
### 步骤
1. 读取 `manifest.yaml`,定位相关模块和依赖关系
2. 读取受影响的核心文件,理解现有实现模式和系统边界
3. 输出设计文档,格式如下:
```
## 设计决策:<功能名>
### 背景
<关联的现有模块>
### 方案选择
<2-3 个方案及取舍,标注选定方案>
### 受影响文件
- 新增:<路径> — <用途>
- 修改:<路径> — <改动说明>
### 系统边界变更
<新增接口 / 数据表 / 字段定义>
### 风险点
- <风险><规避方式>
### 拆解建议
建议拆为 N 张任务卡依赖顺序卡A → 卡B → 卡C
```
4. **不写任何代码**,设计文档经用户确认后再进入拆解流程
---
## 任务拆解流程(/splitter
在设计文档确认后执行,产出可派发任务卡并写入蓝图文件。
### 步骤
1. 确认目标模块 id读取对应 `modules/<id>.md`
2. 按以下原则拆解:
- **粒度**:每张卡 complexity 必须是 S 或 M不允许 L
- **依赖顺序**:数据层 → 后端命令 → 前端封装 → UI 组件
- **可派发标准**:每张卡必须有 `files` + `acceptance` + `complexity`
3. 将任务卡追加到 `modules/<id>.md`
4. 更新 `manifest.yaml`status → `planned`,更新 `updated` 日期
5. 输出拆解摘要卡名、complexity、执行顺序
### 质量检查
- [ ] 每张卡有 `files`
- [ ] 每张卡有 `acceptance`
- [ ] complexity 全部是 S 或 M
- [ ] 有依赖的卡填了 `depends`
- [ ] 任务卡按执行顺序排列
---
## 技能依赖
本蓝图工作流依赖以下 Claude Code 技能处理 L 级复杂任务:
| 技能 | 触发词 | 用途 |
|------|--------|------|
| architect | `/architect` | 复杂功能架构分析,输出设计文档 |
| splitter | `/splitter` | 将设计文档拆解为可派发任务卡 |
### 检测方式
在 Claude Code 中输入 `/architect`,若 Claude 开始执行架构分析步骤则已就绪。
若提示找不到技能,按以下方式安装。
### 安装方式
技能文件随本项目分发,位于 `.claude/skills/`
```bash
# 复制到全局技能目录(任意项目均可使用)
cp -r .claude/skills/architect ~/.claude/skills/
cp -r .claude/skills/splitter ~/.claude/skills/
# 或仅在当前项目使用(.claude/skills/ 已存在则无需操作)
```
> 若通过炼境同步本蓝图规则,`.claude/skills/` 会随蓝图一并分发,无需手动安装。
---
## Rust 代码可测试性规范
> 本规范仅适用于**新增** Rust 函数,存量代码不强制回填。
### 强制规则
**禁止在业务函数内部调用 `pool().get()`**,改为由调用方传入连接:
```rust
// ❌ 禁止(无法在测试中注入 in-memory DB
pub fn my_fn(args: Vec<String>) -> Result<Vec<Foo>, String> {
let conn = crate::db::pool().get()?;
...
}
// ✅ 正确(依赖注入,测试可传入 in-memory DB
pub fn my_fn(conn: &rusqlite::Connection, args: Vec<String>) -> Result<Vec<Foo>, String> {
...
}
// Tauri command 层负责获取连接并调用
#[tauri::command]
pub fn my_command(args: Vec<String>) -> Result<Vec<Foo>, String> {
let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
my_fn(&conn, args)
}
```
### 测试要求
| 函数类型 | 测试要求 |
|---------|---------|
| 纯逻辑函数(无 DB | 必须附带 `#[cfg(test)]` 测试 |
| 含 DB 的业务函数 | 必须使用依赖注入写法,附带 in-memory DB 测试 |
| Tauri command 薄包装层 | 无需测试(只做连接获取 + 函数调用) |
### 任务卡 acceptance 模板补充
含 Rust 业务逻辑的 M/L 任务卡acceptance 须包含:
```
- [ ] 新增函数使用依赖注入conn 作为参数)
- [ ] 附带 #[cfg(test)] 测试,覆盖主路径 + 空数据边界
- [ ] cargo test 通过
```
### 测试写法模板
```rust
#[cfg(test)]
mod tests {
use super::*;
use rusqlite::Connection;
fn setup() -> Connection {
let conn = Connection::open_in_memory().unwrap();
// 建表语句(从 db::migrate 复制对应部分)
conn.execute_batch("CREATE TABLE IF NOT EXISTS ...").unwrap();
conn
}
#[test]
fn test_主路径() {
let conn = setup();
// 插入测试数据
// 调用函数
// 断言结果
}
#[test]
fn test_空数据返回空集合() {
let conn = setup();
let result = my_fn(&conn, vec![]);
assert!(result.unwrap().is_empty());
}
}
```
---
## 注意事项
- 不要删除已完成的任务卡,它们是项目历史的一部分
- 模块 id 一旦确定尽量不要修改(会影响 edges 引用)
- 每次更新后修改 manifest.yaml 顶部的 `updated` 日期
- progress 由桌面 App 自动从任务卡完成比例计算,不需要手动维护
- 当用户的操作隐含了蓝图变更但没有明说时Claude 应主动提议更新(而非沉默)
- **批量执行完成后须做完整性检查**:同一会话内连续完成多张任务卡时,结束前必须逐一确认:① 每张已完成的子任务 status 改为 done前缀改为 ✅;② manifest.yaml 中对应模块的 status 已同步更新。两层缺一不可。(背景:单卡两阶段流程天然同步,批量执行时容易只改模块级 status 而漏掉子任务级更新,导致 App 看板 progress 数据失真)