Files
DevFlow/docs/02-架构设计/滚动规范/功能决策记录-2026-06-14.md
绝尘 998a2f243d 文档: 架构方案文档(意图识别论证+多主题愿景/论证+文档物理分类+边界清晰化)
squash合并:
- 意图识别层论证(8维度+10业界佐证)
- 多主题上下文管理愿景+并存论证+补充论证(多轮agentic)
- 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化)
- 前端架构技术债清单归档
2026-06-19 15:04:04 +08:00

569 lines
86 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.
# 功能决策记录
> 日常开发中对各功能做的**需求规格 + 设计决策规格**(功能粒度,补 [Phase 1 架构决策](./Phase1架构决策-2026-06-12.md) 之下的实现层选择)。聚焦「要做什么 / 为什么这么定」,便于日后回溯。
>
> 创建2026-06-12 | 范围Sprint 510 | 维护:随开发追加
## 约定
**判断标准**3 个月后回看,这条是否仍影响对系统/功能设计的理解?是 → 留本文档;否 → 分流。
**本文档只记两类**
- **设计决策规格**(✅ 已落地 / 🚧 待实测 / 📐 设计未实施)——「为什么这么定」。三要素:决策 → 原因/取舍 → 状态。来源标 `[Sprint N]``[日期]`
- **需求规格 / 待办**(📋)——「要做什么 / 为什么需要」。与 PROGRESS 流水区分:这里记「要做什么 / 为什么需要」PROGRESS 记「做了啥」。
**经验性内容**(踩坑 / 约定 / 技巧 / bug 排查教训)→ [经验记录-2026-06-14.md](./经验记录-2026-06-14.md)。
**老条 / 纯流水 / UX 微调 / 已被取代** → [功能决策记录-归档-2026-06-14.md](./功能决策记录-归档-2026-06-14.md)。
记录规则见 [文档记录规范](./文档记录规范-2026-06-14.md)。
## 人机协同设计基准
### AI coding 下的「过度设计」判断基准 [2026-06-13]
- **决策**:项目为一人开发 + 全程 AI coding人定方向/审查AI 实现),代码被 AI 反复读写。权衡设计时采用新基准——**AI 反复读写的代码,「结构清晰」和「隐性耦合显式化」权重高于传统判断**;但「过度抽象」(多层 trait/Builder/工厂)仍不做,因 AI 读简单直白代码 > 读层层抽象。
- **原因/取舍**:① AI 每次理解代码靠注释和结构比人更依赖1600 行单文件(如 AiChat.vue消耗大量 context拆分反而降低 AI 理解成本;② 隐性依赖(如分离窗口 localStorage 跨 webview人凭经验避开、AI 易踩坑。故:组件拆分从「不做」升为「值得做」;隐性耦合早修或显式标注。③ 规模没到的优化list_all LIMIT、白名单拆表仍按数据量客观判断不因 AI coding 而变。
- **状态**:📐 基准原则(指导后续取舍)
## AI Chat 可靠性
### 流式可靠性三重保险 [Sprint 6 + 2026-06-13]
- **决策**:① reqwest Client 加 `connect_timeout(30s)`**不设请求总 timeout**;② `stream_llm``tokio::time::timeout(120s)` 包**单个** `stream.next()`idle 间隔),不包整个流;③ 前端 stores/ai.ts 加 streaming watchdog——发送启动 60s 计时,收到 AiTextDelta/工具事件/审批结果重置AiApprovalRequired 暂停审批等待不计AiCompleted/AiError 清除60s 无活跃事件则置 streaming=false + push 错误「响应中断」。
- **原因/取舍**:连接阶段防无限 hang总 timeout 会误砍流式长生成任务流式可持续数分钟。idle 120s 防「连上后中途静默」无限 hang只卡单 chunk 间隔,不卡总时长。前端 60s 独立计时(短于后端 120s双保险——后端任何路径漏发收尾崩溃/事件丢失/agent loop 异常退出)前端永久 streaming=true 卡死。审批等待暂停(不计超时)避免误判用户思考。
- **边界**审批组件未渲染见「需求与待办·审批可见性缺口」AiApprovalRequired 暂停后永久卡watchdog 救不了,需审批可见性兜底。
- **状态**:✅ Sprint 6 + 2026-06-13
### 断连丢弃残缺响应
- **决策**:维护 `finished_received` 标志;流尽未收到 finished 信号 → emit AiError 并**丢弃残缺响应,不当完整入库**。
- **原因**:防脏历史污染对话记录(半截回复入库后无法续接)。
- **状态**:✅ Sprint 6
### 停止生成:保留已生成文本
- **决策**`AiSession.stop_flag: Arc<AtomicBool>` + 多检查点响应;停止后**保留已生成文本**。
- **原因**:用户主动停止 ≠ 丢弃成果已输出内容有价值。idle无输出时最多等 120sP2 可用 `tokio::sync::Notify` 优化为绝对即时)。
- **状态**:✅ Sprint 6运行时待实测
### 切对话:从「拒绝切换」演进到「不中断路由」
- **决策**Sprint 6 生成中**拒绝切换**(防 `active_conversation_id` 被改致旧 loop 串台写库)→ Sprint 8 改为**后台对话按 `conversation_id` 路由,生成中可切换不打断**。
- **原因**:拒绝切换体验差;后端给所有 event 加 `conversation_id` + spawn 前快照 conv_id + 前端按 id 路由(后台对话事件不污染当前视图),既不串台又不打断。
- **状态**:✅ Sprint 8部分场景待实测
## AI Chat 工具调用与审批
### Agentic Loop 最多 10 轮
- **决策**`run_agentic_loop` 上限 10 轮(`MAX_AGENT_ITERATIONS`)。
- **原因**:防失控循环;单链 ReAct 10 轮覆盖绝大多数任务。超出需规划式B 路线)。
- **状态**:✅ Sprint 5
### 风险门控Low 自动 / Medium+High 审批
- **决策**:工具按 `RiskLevel` 分级Low 自动执行Medium/High 暂停等人工审批。
- **原因**:读操作放行,写/删操作把关——可靠性 vs 效率的平衡点。
- **状态**:✅ Sprint 5
### `tool_calls` 按 index 排序
- **决策**assistant 消息与 tool_result 两处均按 `index` 排序。
- **原因**:消除 HashMap 迭代乱序致多工具结果错位。
- **状态**:✅ Sprint 6
### 路径校验:拒 `..` 遍历 + 扩敏感目录
- **决策**:正斜杠→反斜杠规范化 + 拒 `..` 路径遍历 + 扩 `.aws`/`.gnupg`
- **原因**最小加固防越权读写根治级workspace 白名单 + canonicalize待边界明确后再做。
- **状态**:✅ Sprint 6边界加固待续
### list_directory 递归防爆:噪音目录剪枝 + 条目上限 + skip_noise_dirs 开关 [2026-06-14]
- **决策**`list_dir_recursive` 递归时跳过噪音目录(`.git`/`node_modules`/`target`/`dist`/`build`/`.next`/`.cache`/`__pycache__`/`.venv`/`venv`/`.idea`)——**列出但不深入内部**;硬上限 1000 条 + `truncated` 标志;默认 `max_depth` 3→2`skip_noise_dirs` 参数(默认 `true`)。
- **原因**AI 广扫项目根传 `recursive:true` 时,`.git`/`node_modules`/`target` 铺平致 13782 项塞进对话 messageUI 卡 + token 爆)。剪枝防爆炸,但保留访问能力:① 噪音目录仍列出(看得见存在 + 大小);② 想看内部时 `list_directory` 直接指向该目录depth=0 起算),或传 `skip_noise_dirs:false` 强制递归进去(仍受 1000 上限 + truncated 保护,适合看编译产物 dist / 运行结果 target 做比对。1000 上限 + truncated 让"想全扫"退化为"分层定点查",不丢信息。
- **状态**:✅ 2026-06-14 落地cargo check 通过)
### `max_tokens` 8192 + `length` 算 finished
- **决策**max_tokens 4096→8192`finish_reason="length"`(截断)纳入 finished。
- **原因**:大任务输出撞 4096 上限被误判断连、丢弃整段响应8192 贴合实际,截断视为正常完成。
- **状态**:✅ Sprint 6
### 审批:删全屏 Modal 保留行内卡片 [Sprint 8]
- **决策**:移除全屏 Tool Approval Modal保留工具卡片内联审批按钮。
- **原因**:全屏 Modal 打断对话流,行内审批更轻量。
- **状态**:✅ Sprint 8
## AI Chat Provider 协议
### 按 `provider_type` 路由 OpenAI / Anthropic
- **决策**:新增 `anthropic_compat.rs` 实现 Anthropic Messages API`provider_type` 分发到 OpenAICompat 或 AnthropicCompat分发处用 `Box<dyn LlmProvider>` trait object。
- **原因**GLM 等订阅端点走 Anthropic 协议(`x-api-key` + 顶层 `system` + 必填 `max_tokens` + SSE content_block统一 Provider trait 屏蔽差异,上层 Agentic Loop / AiNode 零改动(不感知协议)。
- **状态**:✅ Sprint 8用户实测对话流式 OK
### 端点 URL 三段智能拼接
- **决策**`messages_url()` 按 base_url 末段判断——已含 `/v1/messages` 直用;以 `/v1` 结尾补 `/messages`;仅域名(如 `…/api/anthropic``api.anthropic.com`)补 `/v1/messages`
- **原因**GLM 订阅端点 `open.bigmodel.cn/api/anthropic` 与 Claude 官方 `api.anthropic.com` 约定不同(前者无 `/v1`后者需补不强制用户填全路径降低配置门槛。GLM 端点已实测。
- **状态**:✅ Sprint 8
### `max_tokens` 必填兜底 4096 / tool_result 连续合并为一条 user
- **决策**:① Anthropic 协议 `max_tokens` 必填(协议无默认),`DEFAULT_MAX_TOKENS=4096` 兜底OpenAI 协议 max_tokens 可选,缺则报错);② 连续多条 `role=Tool`tool_result累积遇非 Tool 消息 flush 为单条 user 消息含多个 `tool_result` 块。
- **原因**:① 统一兜底避免上层每个调用点都要传值(与 OpenAI Provider 的 8192 上限独立,此处仅缺省兜底)。② Anthropic 要求 tool_result 必须在 user 角色内;多工具并发结果合并为一条 user 而非一对一,贴合协议「一回合一组结果」语义,减少消息碎片。
- **状态**:✅ Sprint 8
### 默认标识is_default 落库为真相源 [Sprint 10 → 2026-06-13]
- **决策**`ai_set_provider` 互斥写 DB目标 `is_default=true`、其余 `false`,仅写变化记录);`ai_save_provider` 新建时若全表尚无默认则自动设为默认(首个);`ai_list_providers` 直接返 DB 值。`session.active_provider_id` 降为运行时缓存,由 `set_provider` 同步,重启清零不影响——`get_active_provider` 兜底取 DB `is_default`
- **原因/取舍**Sprint 10 原 active 作真相源 → 致「重启默认丢失」bugactive 是内存态重启清零,而 `is_default` 字段恒写 false重启后无默认可恢复。`is_default` 字段本为持久化默认而存在回归本职最自然session 持久化需额外存储,重复造轮子。互斥写库保证「唯一默认」语义。
- **边界**:互斥写库逐条 `update_full` **不加事务**——repo 未暴露事务接口;桌面单用户无并发触发,失败即报错、重试自愈。多用户/高并发场景需给 repo 补 `execute_transaction`
- **状态**:✅ 2026-06-13 落地(重启保默认 / 互斥写库 / 首个自动默认实测待补)
### 📋 delete_provider IPC 缺失(前端假删除)[Sprint 10]
- **需求**Settings 删 Provider 当前只前端 filter 移除,不调后端(无 `ai_provider_delete` 命令),重启后配置回归。
- **原因**交互欺骗——用户以为删除成功DB 实际未动。需补 `ai_provider_delete` IPC`lib.rs` 注册 + `ai.rs` 实现 + 前端真调),并加二次确认。
- **状态**:📐 待实施 → ✅ Sprint 10 已实现(`ai_delete_provider` 命令 + 删默认清 active + 自建 `confirmDialog` 二次确认)
### 多 Provider 负载均衡池ProviderPool 归 app 层 + 模型亲和排序 + fallback 分类 [2026-06-17]
- **决策**ProviderPool 实现select/fallback/capacity放在 `src-tauri/commands/ai/` 目录下(非 df-ai crate`prompt.rs::get_active_provider` / `secret.rs::build_provider_for` 同属「消费 AiProviderRecord 的 app 层」。select 排序规则:模型亲和(含 model_id 的 provider 排前)> weight 降序 > is_default 兜底。fallback 策略InitFailedretryable耗尽候选后切下一个 providerFatal4xx 非 429立即放弃整个 fallback 链。per-provider 并发 cap = global_cap差异化 cap 留后续)。否决健康度路由(需持久化健康状态,过度工程;瞬态故障由 fallback 吸收)和纯轮询(加权是轮询超集)。
- **原因/取舍**
- **位置选 commands/ai/ 非 df-ai**df-ai 定位是「协议适配 + Provider trait」存储无关引入 ProviderPool 会创建 df-ai→df-storage 依赖(读 AiProviderRecord破坏存储无关边界。commands/ai/ 本就是 app 层装配点get_active_provider / build_provider_for 都在此层ProviderPool 放此处语义一致。
- **模型亲和排第一**F-01 智能路由已按任务需求选定模型+provider 组合,若 select 排序不含模型亲和可能换到不含该模型的 provider导致路由结果失效。模型亲和保「路由选的模型一定在选中 provider 上可用」。
- **否决健康度路由**:健康检查需持久化状态(上次成功时间/连续失败计数),桌面单用户场景 provider 数量少(通常 2-5 个),瞬态故障由 fallback 重试吸收即可。健康度增加的复杂度(定时探测/状态序列化/启动恢复)远大于收益。
- **Fatal 立即放弃**:对齐 retry.rs 的 Fatal 分类4xx 非 429 = 请求本身非法,重试无意义),避免无效重试浪费 quota 和延迟。
- **状态**:✅ 已落地commit 79b6a43 / b3684f4 / 80c0955
## 模型能力与路由Model Capability & Auto-Routing
### 能力声明:复用 `ai_providers.models` JSON 字段,不建新表 [2026-06-13]
- **决策**:每个 Provider 下可选模型的能力声明(模态/功能/成本等级)存入已有 `ai_providers.models`JSON 数组),**不新建独立表**。新增 `crates/df-ai/src/model_capability.rs` 定义 `ModelCapability`name / modalities / functions / max_tokens / cost_tier+ `TaskRequirements` + `Modality` / `CostTier` 枚举。
- **原因/取舍**:模型能力是 Provider 配置的内在属性,非独立实体——无生命周期管理、无跨表 JOIN 需求JSON 嵌入单行足够Provider 通常 1-5 个);`models` 列已在 V9 建表且全链路预留,复用零 schema 变更;独立表需外键/级联/JOIN对桌面应用过度工程备份迁移友好配置自包含一行。代价无法 SQL 查询「所有有 vision 的模型」,但此查询当前和近期均不需要。
- **状态**:📐 设计未实施Phase 1数据模型 + 场景级路由)
### 路由层:纯函数 ModelRouter重写现有骨架 [2026-06-13]
- **决策**:重写 `crates/df-ai/src/router.rs` 已有但空的 `ModelRouter`——从 `TaskRequirements` + Provider model_pool → 按硬性要求筛选候选 → 按 cost_tier 升序取最便宜。路由是同步纯函数(无 I/O、无全局可变态可单测。7 个 LLM 调用点统一接入。`override_model` 字段支持用户显式指定(聊天手动切)和工作流节点 config.model节点作者指定两种覆盖优先级最高。
- **原因**:路由是确定性计算(静态配置→模型名),无需 async/service 化;纯函数可单测;统一入口避免散装 if-else。
- **各调用点路由策略**:主对话 `chat(has_image)`→Standard/Premium标题生成 `title_generation()`→Economy 最便宜;知识提炼 `knowledge_extraction()`→Economy/Standard工作流 AiNode `workflow_node()` + override=config.model→节点指定优先**Embedding (×2) 不走路由器**(独立路径,不同模型类)。
- **向后兼容**`models=None`(老记录)→ model_pool 空 → 所有 route 返回 default_model → 行为不变。
- **状态**:📐 设计未实施Phase 1
### Embedding 不进通用路由器 [2026-06-13]
- **决策**Embedding 模型不走 ModelRouter继续走独立路径`KnowledgeConfig.embedding_model` + `embedding_provider_id`)。
- **原因**Embedding 是完全不同的模型类——API 不同(`/v1/embeddings` vs `/v1/chat/completions`)、用途不同(向量化 vs 生成)、通常更小更专用。混入通用模型池会混淆用户并增加路由分支复杂度。
- **状态**:📐 设计未实施Phase 1 确认不动 embedding 路径)
### 分阶段实施路线 [2026-06-13]
- **Phase 1**(本次):数据模型 + ModelRouter 重写 + 7 调用点接入 + Settings 模型池编辑 UI + AiChat 模型下拉。ChatMessage.content 保持 String 不改。
- **Phase 2**后续多模态消息——ChatMessage.content: String → Vec<ContentPart>(Text/Image);前端粘贴/拖拽图片vision 模型自动路由。
- **Phase 3**后续Agent 内智能路由——Agentic Loop 每轮按子任务构造不同 TaskRequirements成本预算控制模型级联降级跨 Provider 搜索。
- **状态**:📐 设计未实施
### 📋 模型能力系统 — 完整改动文件清单 [2026-06-13]
- **后端 Rust**`crates/df-ai/src/model_capability.rs`**新增** ModelCapability / TaskRequirements / Modality / CostTier/ `router.rs`**重写** ModelRouter 匹配逻辑)/ `lib.rs`re-export/ `df-storage/src/migrations.rs`V10 版本号推进,无需 ALTER/ `src-tauri/src/commands/ai.rs`7 调用点加 routerai_save_provider 加 models 参数;新增 ai_set_chat_model_override
- **前端 TS/Vue**`src/api/types.ts`(新增 ModelCapability / Modality / FunctionCapabilities / CostTier 类型)/ `src/api/ai.ts`saveProvider 加 models 参数;新增 setChatModelOverride()/ `src/stores/ai.ts`availableModels / activeModelOverride 状态 + setModelOverride action/ `src/views/Settings.vue`Provider 表单增加模型池编辑区)
- **状态**:📐 待实施Phase 1 全量清单)
### 多模态消息content:String 保留 + parts 新增,务实偏离 Vec<ContentPart> 原案 [2026-06-17]
- **决策**Phase 2 多模态原设计 `content: Vec<ContentPart>` 改为 **`content: String` 保留不变 + 新增 `parts: Option<Vec<ContentPart>>`**。老 JSON 反序列化时 parts=None 零回归。后续若严格对齐 content:Vec 需先解禁 4 个 forbidden 文件的 content 消费点(改用 content_text() / flattened_parts() 辅助方法)。
- **原因/取舍**
- **4 个 forbidden 文件直接消费 m.content:String**——`audit.rs`(审计日志读 content/ `title.rs:50`(结构体字面量 ChatMessage{ content: ..., role: ... }/ `commands.rs`IPC 层序列化)/ `knowledge_inject.rs`(知识注入读取)。其中 title.rs 是结构体字面量构造Rust 不支持字段默认值,加任何 required 字段必炸编译。
- **不改 content 为 Vec 的代价可控**——parts 携带多模态数据content 保留纯文本降级路径。前端/LLM 层按 parts 是否 Some 判断是否多模态,老路径不受影响。
- **向后兼容**——serde `#[serde(default)]` 让缺失 parts 字段的老 JSON 反序列化为 None零回归风险。
- **状态**:✅ 已落地commit e3cd448
## 灵感模块(评估闭环)
### 启发式评分维度
- **决策**:三维固定 5.0 → 内容启发式priority / 描述充实度 / tags / 中英关键词clamp 0-10。
- **原因**:固定值无区分度;启发式基于 idea 内容给差异化评分。
- **状态**:✅ Sprint 9启发式未接 LLM
### 对抗评估:启发式 fallback 待接 LLM
- **决策**:正反方论点/evidence 基于真实 idea 内容生成confidence 由评分驱动;未接 df-ai LlmProvider。UI 诚实标注——评估区标题加「启发式」黄标(`.eval-mode-tag`),对齐实现深度避免名实不符,接 LLM 后摘除。
- **原因**先打通评估闭环LLM 生成论点 + 启发式 fallback 为后续增强。
- **→ 接入方式已定**:走全局 AI trait 下沉(见 [crate 治理 — AI trait 下沉拆 df-ai-core](#ai-trait-下沉拆-df-ai-core-轻层确立全局-ai-接入标准-2026-06-14-))。`evaluate()``LlmProvider` 注入参数,启发式降级为 fallbackF-03 原 A/B/C 选型据此收敛为「全局统一 trait」。
- **状态**:📐 设计未实施LLM 接入)
### 前后端标签对齐
- **决策**`recommendation` 全小写空格(后端原 "With Resources" 不匹配前端 map key`assessmentClass` 映射到 CSS 类名(`.immediate`/`.soon`/...)。
- **原因**:大小写/类名不一致致中文标签不显示、badge 无色。
- **状态**:✅ Sprint 9
### 评分 0-10crate→ 0-100前端IPC 缩放 + 多字段写回用单事务 [Sprint 9/10]
- **决策**:① crate 内 IdeaScores 维持 0-10IPC 层 evaluate_idea 组装 scores JSON 时 *10 缩放为 0-100并用中文维度键可行性/影响力/紧急度/综合)。② evaluate_idea / promote_idea 写回想法用 `update_full`(单事务覆盖整条记录),放弃多次 `update_field`
- **原因**:① 前端雷达图直接当百分比渲染、零前端改动crate 内 0-10 符合评分直觉IPC 层做单位适配。② 多次 update_field 各自独立连接中途失败致数据半成品update_full 原子。
- **状态**:🚧 Sprint 9/10编译/构建通过,未 tauri dev 实测)
## 想法立项promotion
### 复用 df-project 领域层crate do_promote 留纯决策 TODO [Sprint 10]
- **决策**`promote_idea` IPC 复用 `df_project::manager::ProjectManager::create_from_idea` 构造项目实体 + 映射 ProjectRecord 持久化 + update_full 回写想法df-ideas crate 内 `IdeaPromoter/do_promote` 保留纯决策 TODO不真正创建项目。
- **原因**crate 不依赖 df-storage/df-project避免循环依赖、保持可单测手动立项无需 Auto/Manual/SemiAuto 策略判断(用户点即确认),副作用放 IPC 组合,与 evaluate_idea 同模式crate 纯决策留待自动/半自动晋升场景复用。
- **状态**:🚧 Sprint 10编译/构建通过,未 tauri dev 实测)
### promoted_to 非空拒绝重复立项 [Sprint 10]
- **决策**promote_idea 取想法后校验 promoted_to 已存在则返回错误,不创建新项目。
- **原因**:防同一想法多次点「立项」生成多个项目;幂等保护。
- **状态**:🚧 Sprint 10
## 项目管理(删除 / 回收站)
### 项目删除软删回收站deleted_at + 应用层级联),非物理删 [2026-06-13]
- **决策**:删项目改为**软删**——`projects``deleted_at TEXT`V11 迁移),`delete_project``deleted_at`(进回收站,可恢复);`list_projects` 过滤 `deleted_at IS NULL`;回收站(`list_deleted_projects`)可「恢复」(清 deleted_at或「彻底删除」`purge_project` 事务级联物理删 branches→releases→tasks→projects不可逆。子表软删时不动FK 仍满足,项目数据完整保留待恢复。
- **原因/取舍**:① 用户要求「可恢复」——纯 CASCADE 物理删不可逆,误删难挽回;软删 + 回收站给反悔余地。② SQLite `ALTER TABLE` 改不了已有表 FK 约束,给老库 projects 加 `ON DELETE CASCADE` 须重建表(高风险),故不走 DDL 级联,改**应用层级联**purge 时事务内顺序删子表),语义等价且可测、不依赖 `PRAGMA foreign_keys`。③ 软删只标记 projects 行、子表不动——恢复时项目连同历史任务/分支/发布完整还原。④ 两级风险分级:日常软删可逆 / 回收站 purge 二次确认后物理删不可逆。
- **边界**`ProjectRecord` 不带 `deleted_at` 字段,纯靠 SQL `WHERE deleted_at IS NULL` 过滤models/types 零变更。`ai.rs build_system_prompt` 同步改用 `list_active`(防软删项目泄漏进 AI 上下文)。`soft_delete`/`restore` 守卫对称,重复操作幂等。
- **状态**:✅ 2026-06-13 落地V11 迁移 + ProjectRepo 5 方法 + 3 IPC 命令 + 回收站 modal + 删除入口cargo check + vue-tsc + 11 integration test 全绿)
## 项目管理(目录绑定 / 技术栈探测)
### 项目绑定真实代码目录:扩 ProjectRecord + df-project scan [2026-06-13]
- **决策**`ProjectRecord``path`(绑定目录绝对路径)+ `stack`(技术栈 JSON 数组字符串两字段V12 迁移nullable技术栈探测逻辑放**新建 `df-project/src/scan.rs::detect_stack`**(纯函数,浅读根目录标志文件识别 rust/go/python/java/csharp/vue/react/angular/svelte/next/vite/typescript/node/tauricommands 层薄封装 4 命令(`scan_project_stack`/`check_path_binding`/`relocate_project_path`/`check_path_exists``check/relocate``canonicalize` 规范化路径防绕过重复检查。新建项目可选绑定目录→自动探测栈,详情页支持重定位 + 目录失联检测 + 防重复绑定。
- **原因/取舍**:① **扩 df-storage ProjectRecord 而非激活 df-project ProjectContext 空壳**——`ProjectContext` 虽早设计 `root_path`/`tech_stack`/`repo_url`/`ai_context` 字段,但 `manager.rs` 标注 TODO 从未接存储/运行时;激活需新建 `project_contexts` 表 + 填充暂不需要字段,第一步过重,守 YAGNI。ProjectRecord 是实际运行链路,直接扩最快见效。② **scan 放 df-project 而非 commands 层**——`ProjectContext.tech_stack` 本就是 df-project 职责字段scan 是其天然能力且可被 df-ai/df-workflow 复用,放 commands 变一次性代码。③ **migration nullable**——老项目 path/stack=NULL 零影响。④ **一致性原则**:先在「新建流」验证,第二步「导入历史项目」复用同一套 scan/relocate/checkBinding。
- **边界**`path` 规范化canonicalize仅用于比较存库保留用户输入的原始可读路径。程序化创建项目想法晋升、AI 工具path/stack = None不绑定目录`df-project``ProjectContext` 刻意未激活ai_context/repo_url 等暂留空)。
- **状态**:✅ 2026-06-13 落地V12 迁移 + detect_stack + 4 IPC 命令 + 选目录/查重/卡片栈 + 重定位/目录状态cargo build + scan 4 单测 + vue-tsc 全绿)。📋 导入历史项目(第二步):复用 scan/relocate/checkBinding加 monorepo 子目录识别 + README 首段抽 description + 批量。
### 导入/绑定项目 AI 扫描填信息:规则探测兜底 + LLM 增强,采样与 LLM 调用分层 [2026-06-14]
- **决策**:导入/绑定项目时规则 `detect_stack`(快/免费/准)必跑兜底 + LLM 分析采样README+目录树+清单,不读源码)产出 description 摘要与 stack 细化LLM 失败降级纯规则。采样逻辑放 df-project纯 IOLLM 调用放 commands 层。
- **原因/取舍**:① 规则兜底保证 LLM 不稳定时仍有基础信息LLM 只补规则搞不定的摘要。② 采样与 LLM 分层——采样纯 IO 属 df-project 职责可复用LLM 调用依赖 df-ai放 commands 使 df-project 保持无 LLM 依赖(防循环)。③ 不读源码控 token+隐私。④ 结果预览让用户把关防 LLM 瞎编。
- **状态**:✅ 2026-06-14 落地scan_project_with_ai 命令 + 前端 AI 扫描预览;编译/单测/类型全绿)。
### 导入历史项目scan 第二步设计description 走 LLM + 采样保留内容图 + monorepo 一层 + 批量并发 [2026-06-14]
- **决策**F-06 = scan 第二步,选根目录 → 发现项目(含 monorepo 子目录)→ 勾选批量导入。六点收敛:① **description 走 LLM** 复用 `scan_project_with_ai`command 层 complete**不做纯规则抽取**(跨 README 格式 brittle**采样改进**——`ProjectSample``images: Vec<ImageRef{alt,src}>``readme` 剥 frontmatter/TOC/纯徽章行后截 ~8KB`SAMPLE_README_MAX=2000` 偏小粗暴),**保留内容图 markdown 原样**;③ **image 多模态条件化**——当前 `ChatMessage.content:String`F-260614-05 未做)走纯文本降级,采样层先不丢 image 引用留接口Phase 2 上线后读 base64 喂 vision**monorepo 一层识别**`is_monorepo` 检 pnpm-workspace/lerna/turbo/nx + package.json workspaces`discover_projects` 展开 packages/\*/apps/\* 直接子目录,`detect_stack` 空的过滤);⑤ **批量流程**——`scan_directory_for_projects` 规则发现(快、不跑 LLM+ 标已绑定项;用户勾选后 `import_projects_batch` 对勾选项**并发** LLM 抽 description`llm_concurrency` 双层 permit 限流)+ 复用绑定入库,非原子逐项独立;⑥ **对称改进**——抽内部 `create_with_binding`create_project + import batch 共用「校验+防重+探测+insert」缓解决策记录:211 TODOrelocate 不并入update 非 insert
- **原因/取舍**:① description 纯规则抽首段会撞徽章墙/多语言引导/TOC——抽出来是噪音语义抽取归 LLM**image 不能粗暴跳过**(修正原 plan 错把 image 归噪音)——架构图/截图是 description 关键信息一张顶千字只跳徽章shields.io/badge.fury 等域 + build/version/license/coverage 关键词);③ 采样不丢 image = F-06 不被 F-260614-05 阻塞但不留遗憾,两者配套;④ 批量只对勾选项跑 LLM远少于发现全量平衡速度质量⑤ 「子代理」= 轻量 complete 复用现有 `scan_project_with_ai` 路径,非 aichat ReAct 重 agent批量精修不值得上多轮
- **边界**:导入项目 status 默认 `planning`(对齐 create_project导入后手改预览表格只读name/desc/stack/已绑定标记,不展示 image导入后详情页改不关联 idea批量无实时进度条最终 toast 汇总(导入 N/跳过 MLLM 全失败 description 留空让用户手填(不喂噪音)。
- **状态**:📐 2026-06-14 设计定稿待实施6 决策经 3 轮讨论收敛,修正原 plan 两处草率:纯规则 description + 跳 image。📋 实现时df-project 加 `discover_projects`/`is_monorepo` + `collect_sample` 扩 images + 徽章过滤commands 加 `scan_directory_for_projects`/`import_projects_batch` + 抽 `create_with_binding`;前端 Projects.vue 加导入 modal + i18nscan.rs 单测(采样剥噪音/image 收集/monorepo/discover
### AI 工具绑定目录bind_directory 专用工具 + 工具层白名单同步 + prompt 禁冒充 [2026-06-14]
- **决策**:① update_project 工具白名单补 path/stack同步 db 新字段);② 新增 bind_directory 专用工具(绑定目录不走通用 update③ 系统 prompt 加约束:工具失败须明说,禁用替代操作冒充原意图成功。
- **原因/取舍**:① review AI 对话发现 update_project(path) 被工具层白名单拒db 字段加了但工具层漏同步AI 转而改写 description 却回复「已记录」冒充绑定成功误导用户。② 专用工具语义清晰,防 AI 走通用 update 捷径冒充。③ prompt 约束防单链 ReAct「自我圆场」幻觉失败时用替代谎报成功
- **状态**:✅ 2026-06-14 落地(白名单同步 / bind_directory / prompt 中英约束;编译全绿)。📋 待清:工具层白名单与 crud 白名单双份去重(详见经验记录)。
### 📋 项目管理 review 剩余问题与处理论证(供后续会话)[2026-06-14]
- **背景**:项目管理 review 12 条9 条已修①delete 软删 / ②回收站工具 restore+purge+list_trash / ③collect_sample spawn_blocking / ④normalize_path 抽公共 / ⑥i18n / ⑦parseStack 抽 utils / ⑧ConfirmDialog / ⑫create_project 加 path。剩余 4 条 + 1 新发现论证如下,设计视角取**全局 + 对称 + 优雅**,非局部最优。
- **值得改(全局必要 + 对称缺失)**
- **⑤ update 白名单双份**tool_registry update_project 硬编码 5 字段 vs crud allowed_columns已致一次 bug。对称论证两者**语义不同**——DB 白名单=SQL 安全列(含 id/created_at/idea_id 系统字段AI 白名单=业务可改子集。不能复制,应**派生**AI ⊂ DB减系统字段真相源在 DB 一处。当前平行两份不对称,必漂移。
- **⑩ scan_project_with_ai 无 LLM 超时**。全局complete 卡住占 `llm_concurrency` 全局 permit → 阻塞主对话/标题生成/知识提炼,不止单次扫描。对称:`stream_llm` 有 idle 120s timeout见「流式可靠性三重保险」complete 非流式却无——两套 LLM 超时策略不对称,应对齐。
- **🆕 create_project 与 bind_directory 绑定逻辑重复**tool_registry create:170 内联绑定 + bind_directory:220 重复,已标 TODOcommands/project.rs create/relocate 同样)。对称+优雅:绑定是单一子操作,应集中 df-projectnormalize_path 已归此create/bind/relocate 共用一个 bind fn。当前 create 内联 bind 破坏「同名操作同实现」的对称。
- **低优先(局部优化,降级兜底,过度反伤优雅)**
- ⑨ collect_sample 文件大小限制read_to_string 全读再截,实际 README/清单 <10KB 概率低。最多一行 `metadata skip >1MB`,不必过度。
- ⑪ parse_scan_result JSON 提取:单 JSON 对象 OK多段场景降级兜底空 desc+规则 stack已足够。
- **状态**:📋 待后续会话处理。优先级 ⑤⑩ + create/bind 去重(中,全局/对称必要)> ⑨⑪(低/可选)。
## 知识库df-evolve / 共享记忆层)
> 核心定位与设计决策。详细字段/参数级决策见各条目。
### 定位 + 被动 Service 退化 [2026-06-13]
- **决策**知识库df-evolve定位为**整个 DevFlow 的共享记忆层**——每个模块idea/task/workflow/review/chat既是知识生产者也是消费者而非孤立展示功能页。df-evolve 褫夺「自动进化引擎」角色Sprint 2 对抗论证已砍,自用阶段 ROI 低/过度工程),**退化为被动 Service 层**,只暴露 `search/retrieve/record_reuse/feedback/save` 供各模块调用;被动 Service 复用 df-ai 检索做消费侧EventBus 做事件驱动提示(非自动抓取)。
- **原因**:手脑(各业务模块)分离无学习能力;知识库做「肌肉记忆」中枢系统才越用越懂你。砍的是自动挖矿,非知识库本身。
- **状态**:📐 设计未实施(用户拍板定位)
### 沉淀审核机制知识状态机AI 只产草稿 [2026-06-13]
- **决策**:知识加 `status` 字段,状态机 `candidate → pending_review → published → archived`**AI 提炼的知识一律进 candidate绝不直接入正式库**;草稿进「待审核收件箱」,人工逐条编辑(内容/分类/标签)→ 发布或丢弃。`verified` = 发布审核时一次性人工标intake 决断动作,非 ongoing 评分)。沿用 IdeaRecord 的 `pending_review` 状态机模式。
- **原因**沉淀必须有人工把关用户要求「人工能够编辑或调整必须有这些过程」——AI 提议、人裁决、系统如实记,非黑箱自动学习。
- **状态**:📐 设计未实施(用户明确要求加审核机制)
### AI 提炼产出:字段集 + 置信度(唯一指标)+ 查重 [2026-06-13]
- **决策**AI 提炼一条 candidate 时产出——**内容字段**`kind`分类AI 判定 7 类之一)、`title``content``tags``source_ref`(原始证据片段);**质量指标**`confidence`High/Medium/LowAI 自评,**唯一质量指标**);提炼时另做**查重**(比对 published 库,重复则不产/标合并,非存储字段)。**克制边界**:质量指标只留 confidence——不加 generality/specificity/novelty 等维度(过工程化 + 多耗 token适用范围并入 `tags` 不单列 scope 字段。
- **原因**`kind``source_ref` 是 AI 必填但易漏的两项confidence 服务降噪 + 审核分诊 + 透明,但**不绕过人审门**high 也不自动发布)。
- **状态**:📐 设计未实施
### 透明化provenance 溯源 + 收件箱 + 注入告知 [2026-06-13]
- **决策**:① 每条知识标来源(哪次 Chat/task/review 产出 + 原始片段),可跳回——复用现有 `source_project` + `source_ref` 字段;② 「待审核收件箱」作明确信息渠道;③ 复用时显式告知本次注入了哪几条、为什么命中。
- **原因**用户要求「透明化让人们有很好的信息获取渠道」——不黑箱。provenance 字段现有模型已有,零新增成本。
- **状态**:📐 设计未实施
### 克制原则:宁缺毋滥,小步迭代 [2026-06-13]
- **决策**:① **检索注入保守**——精确匹配(标签/关键词)优先,语义模糊匹配**后做**top-N 限 1-3 条,置信不够一条都不塞;② **关联不自动推断**——IdeaGraph 自动聚类/关联发现**先不做**,只支持人工标注;③ **沉淀不主动监听全量事件**——仅「一键沉淀」或事件提示后「确认」才产 candidate**从小到大**——先 AI Chat 单点双向跑通验证手感,再串 review/idea。
- **原因**:用户要求「尽可能克制,不要做大胆的连接,从小到大」——贯彻 Sprint 2「scope 砍 60%」精神到知识库,避免重蹈「自动进化」过度工程覆辙。
- **状态**:📐 设计未实施(用户明确要求克制)
### 指标客观化reuse_count 唯一信号,撤销 effectiveness 人工评分 [2026-06-13]
- **决策**:知识排名/淘汰**只用 `reuse_count` 一个客观信号**(检索注入自动 +1**撤销 `effectiveness` 的人工 👍/👎 评分**主观、有摩擦、信号不准淘汰改客观——reuse_count=0 且超 N 天未用 → 提示归档。
- **演进**初版三指标reuse_count + effectiveness + verified共同排序权重 → 同日修正:用户指出 👍/👎 是「人为、主观、非准确」的非必要干预,撤销。「用过 ≠ 有用」的质量顾虑改由两层客观兜底——① intake 审核门(一次性决断)② 发现噪音直接删。召回不准根因在标签/搜索质量,靠 intake 打准标签解决。
- **状态**:📐 设计未实施
### 📋 分层落地 Tier 1/2/3 + 来源/去向审查 [2026-06-13]
- **需求**:知识库联动分三层——**Tier 1必做**AI Chat ↔ 知识库双向 + 手动录入(沉淀 + 检索注入 + reuse_count + 审核收件箱 + 状态机,**无人工评分****Tier 2串创作流带前置**ai_node 检索 prompt_template、工作流 NodeFailed → pitfall**仅失败时**)、决策记录 → architecture_pattern**前置:先补 df-traceability 持久化****Tier 3**evolve_engine 自动挖事件。
- **审查(来源)**:① **/review 源移出**——devflow 无代码审查功能df-stages/coding.rs 审查节点是 TODO 空壳);② **想法评估源存疑**——对抗论点是「一次性结论」非可复用知识;③ **决策记录源标前置**——`DecisionJournal` 所有 SQLite 查询 TODO 未持久化;④ **工作流源限定失败时**——运行日志≠提炼知识。
- **审查(去向)****Chat 轴是唯一 Tier 1 就绪消费端**(检索→注入对话/提示词ai_node prompt 注入属 Tier 2工作流无主动消费仅被动产 pitfall决策溯源消费半残。→ 来源/去向双收敛到 Chat 轴。
- **状态**:📐 待实施(先做 Tier 1Chat + 手动录入)
### 对外暴露MCP Server 双向协议 [2026-06-13]
- **决策**:知识库对外 API 采用 **MCP Server** 形式暴露,**双向**(读+写。Tier 1 先做 DevFlow 内部闭环Tauri IPC**命令层设计完全对齐 MCP 语义**Tier 1+ 套 MCP server封装已有 command。MCP 暴露:① **Resource (读)**list/search/get**Tool (写)**create_candidate**Tool (计数)**record_reuse。
- **原因**:① Claude Code 原生吃 MCP——本机主力工具零集成成本② Cursor 也支持 MCP**双向价值**:外部工具(尤其 Claude Code 做代码审查/重构时是高质量知识来源——审查结论→review_rule、踩坑经历→pitfall接 MCP 自动回流知识库等于开「第二来源入口」。克制Tier 1 不实现 MCP 本身,但 6 个核心命令search/list/get/create/update_status/record_reuse全部按可暴露设计。
- **状态**:📐 设计未实施Tier 1+ 事项)
### 矛盾知识处理:纯标签+内容自述source_project 仅溯源 [2026-06-13]
- **决策**:矛盾知识**不建冲突关系表、不加 scope 字段、不做 access control**。消歧靠 tags`["Go","微服务"]` vs `["Go","单体"]`+ content 自述适用范围 + source_project 仅作来源溯源展示不参与检索过滤。AI 提炼 prompt 加约束:「适用范围有限制必须在 content 或 tags 中标注」。零数据结构变更。
- **原因/取舍**:矛盾是少数场景,为 minority 建关系系统是过度工程source_project 若做绑定/过滤会提高维护门槛 + 降低通用知识复用率;检索 top-N≤3 返回时内容本身场景描述足够消费者判断。
- **状态**:📐 设计未实施
### AI 提炼触发 + 知识注入:可配置 [2026-06-13]
- **决策**:① AI 提炼**默认自动触发**(后台 detached task4 个配置项:`auto_extract`default true/ `trigger_mode`on_complete|on_idle|manual_onlydefault on_complete/ `min_messages`default 4/ `idle_timeout_ms`default 30000。② Chat system prompt 知识注入**加开关**`auto_inject: bool`default true关闭时首行返回空字符串零开销
- **原因/取舍**手动按钮依赖用户记得点→遗忘→空库死循环自动触发保证持续流入候选。但用户控制欲不同——4 配置项覆盖从「全自动」到「全手动」。每次 complete() <500 input/<200 output token可关零成本。注入开关给用户「先积累再开启 / 调试不被干扰」的控制权。
- **状态**:📐 提炼设计未实施 / ✅ 注入已实施Tier 1
### 检索方案LIKE + top-N向量检索提前到 Tier 1Phase 5.5[2026-06-13]
- **决策**Tier 1 检索用 SQLite `title/content LIKE '%query%'` + `ORDER BY reuse_count DESC LIMIT 3`;收件箱排序用 `CASE WHEN confidence 'high'→3/'medium'→2/'low'→1`(非纯 TEXT 字典序)。**向量检索从 Tier 2 提前到 Tier 1 同步实施**Phase 5.5),加 **Settings 开关**`vector_enabled`,默认 false
- **原因/取舍**:知识库核心消费场景是 AI 自动注入(拿用户自然语言 query 检索非关键词精确搜索——「部署后白屏」匹配不到「Nginx SPA 路由」,纯 LIKE 语义盲区从第一天就存在。开关化解「本地优先/零依赖」哲学冲突:默认关闭纯 LIKE 零外部调用,开启后才走 embed API。
- **实施细节**:① `LlmProvider` trait 加 `embed()`OpenAICompat 实现Anthropic 不支持);② V8 幂等补 `embedding BLOB`f32 小端序列化NULL=未嵌入走 LIKE**嵌入时机=发布时**candidate 不浪费 embedpublished 才参与检索);④ 纯 Rust 余弦(<50k 条暴力遍历够用,不引 sqlite-vec 避免 Windows C 扩展编译风险);⑤ 三层降级链开关关→LIKE开但 provider 缺/embed 失败→自动回 LIKE正常→混合检索双信号>LIKE 单>向量单cos≥0.3 滤噪)。
- **状态**:✅ Tier 1 LIKE + 向量混合检索Phase 5.5)均已实施,编译通过待实测
### 知识生命线:独立 knowledge_events 表(非 JSON 嵌主表)[2026-06-13]
- **决策**:知识产生/审核/引用/归档四类审计事件存**独立 `knowledge_events` 表**V10 迁移),而非塞进 `knowledges.context_json` 字段。
- **原因/取舍**:事件是追加型(只增不改删),语义与主表 CRUD 完全不同一条知识可被引用数百次JSON 嵌主表致行膨胀 + 写更新竞争(每次引用都重写整行)。独立表可建 `(knowledge_id,event_type)` 复合索引事件表写失败只丢审计、不影响知识本身fire-and-forget 隔离)。未来加新事件类型只加一行 insert不动主表 schema。
- **状态**:✅ 已实施V10 迁移 + KnowledgeEventsRepo + 前端生命线时间线)
### 📋 知识详情页 + 编辑能力candidate 审核闭环)[2026-06-13]
- **需求**:卡片点不开详情、不能编辑、看不到「为什么产生」。详情页需呈现完整生命线 + candidate 可编辑修正后发布。
- **决策**Knowledge.vue 重构为 Ideas 式左右分栏(左卡片列表 @click 选中 / 右详情面板四分区:①基本信息 ②溯源 ③引用记录 ④生命周期时间线);可编辑字段 title/content/tags/confidence/reasoning 走 `knowledge_update`(部分更新)。
- **原因/取舍**candidate 编辑是审核闭环刚需——AI 提炼必有水分/措辞瑕疵,只能原样发布或整条拒绝会让审核空转。详情复用 Ideas 已验证的 master-detail 模式。published 编辑不做(有归档+重提炼替代)。
- **状态**:✅ 已实施(编译+vue-tsc+df-storage 21 单测全绿GUI 实测待 #54
## AI Chat 上下文窗口与并发控制(架构结论)
> 实现细节见 [归档文档](./功能决策记录-归档-2026-06-14.md)「AI Chat 上下文窗口与并发控制」。
### 设计决策 [2026-06-13]
- **ContextManager 类型替换为 messages 真相源**`AiSession.messages``Vec<ChatMessage>` 改为 `ContextManager`(非 wrapper 包装层),消息真相源唯一,避免 Vec + ContextManager 双存导致状态分裂。裁剪仅影响发送视图(`build_for_request` 返回裁剪版,`all_messages_clone` 返全量落库)。
- **淘汰算法:分组滑动窗口 + 三元组保护**`Assistant(tool_calls) + Tool(result)* + Assistant(final_text)` 工具调用三元组作为原子整体;最后 6 条≈2 个完整用户轮次)设为保护区永不淘汰。
- **Token 计数零依赖**`chars_count × 0.35` 粗估(误差 ±15% 可接受),不引入 tiktoken-rs5MB BPE 数据文件对 Tauri 打包不友好)。
- **双层 Semaphore 并发控制**AppState `LlmConcurrency`——全局并发默认 3 / 单对话默认 2permit 在 3 个叶子 LLM 调用点 acquire`run_agentic_loop` stream_llm 前、`generate_title_via_llm``extract_knowledge_from_conversation`),工具执行不受控。`per_conv` 实为应用级单一信号量(因 AiSession 单例 + generating 互斥,命名宽泛但当前语义正确,多对话路线时改 HashMap。Semaphore 重建用「软收敛」策略(替换内层 Arc旧 permit 不受影响)。
- **裁剪策略与模型选择正交**ContextConfig 不含 mode/模型选择字段;「高精度/低精度对话」属 LLM 调用层参数,与裁剪策略是正交维度。
- **状态**:✅ 已落地2026-06-13cargo check + vue-tsc 通过,待 tauri dev 实测)
## i18n
### legacy:false + zh-CN 默认 + locale 拆分 + glob 聚合 [Sprint 7 + 2026-06-13]
- **决策**:① `legacy:false` / `globalInjection` / zh-CN 默认 + en fallback / `localStorage df-language` 持久化。② `zh-CN.ts`/`en.ts` 单文件 → `zh-CN/*.ts` + `en/*.ts` 按模块拆分nav/dashboard/ai/common/settings/ideas/knowledge/projects/projectDetail/tasks/aiChat/aiTool`index.ts``import.meta.glob('./*.ts', { eager, import: 'default' })` 自动聚合(排除 index 自身)。新增模块文件即生效,不改 index。
- **原因**:① Composition API 模式;默认中文贴合自用,英文兜底。② 全量 i18n 接入8 view ~640 处中文)用多代理并行,模块隔离零冲突(每代理建自己模块 + 改自己 view不动共享 index比单文件扩 key多代理改同一 `zh-CN.ts` 冲突更适合并行。glob eager 运行时聚合,动态新增模块即拾取。
- **状态**:✅ Sprint 7 + 2026-06-13curl 验证 vite 正确展开 globvue-tsc PASS
### 状态枚举 i18nconstants 存 keyview 包 $t
- **决策**`constants/project.ts``PROJECT_STATUS_LABELS`/`TASK_STATUS_LABELS` 值从中文文案改存 i18n key`planning: 'projects.status.planning'``projectStatusLabel`/`taskStatusLabel` 返回 keyview 显示处包 `$t(projectStatusLabel(x))``PRIORITY_LABELS`P0/P1是代号非文案不动。
- **原因**constants 是纯数据/结构层,不应含展示文案(违反分层);文案归 localeconstants 管映射结构。
- **状态**:✅ 已落地
### 📋 项目 status 字段语义混乱(生命周期 vs 开发阶段)
- **现象**:后端 `projects.status DEFAULT 'active'` + `list_active`/软删除active/deleted 生命周期),但前端 `PROJECT_STATUS_LABELS` 是 planning/in_progress/paused/completed/cancelled开发阶段两套塞一个 status 字段。DB 实际只有 `status='active'`(新建默认,阶段值从没产生),前端 map 不认 → 显示英文 "active"。
- **治标**:补 `projects.status.active`(🚀进行中),不再显示英文。→ 根本未除。
- **治理方向**:① 后端支持阶段流转planning→in_progress…② 前端 map 对齐后端真实值active/deleted/archived③ 拆双字段status 生命周期 + stage 开发阶段)。
- **状态**:📐 待治理(治标已落地,根本病根未除)
### i18n import 统一 `@/i18n` 路径别名 [2026-06-15]
- **决策**vite.config.ts 加 `resolve.alias.{ '@': '/src' }` 路径别名;全项目 i18n import 统一为 `import i18n from '@/i18n'`,替代相对路径 `../i18n`/`../../i18n`
- **原因/取舍**CR-08 i18n 批量改造时workflow 代理将 stores/ 下层文件 i18n import 从相对路径(`../../i18n`)改为错误多层的 `../../../i18n` 或正确的 `@/i18n`(但项目无别名配置),导致 vite 构建失败(Could not resolve)。相对路径随文件深度变化易断stores/ 两层 vs composables/ai/ 三层 vs utils/ 一层);`@/i18n` 绝对路径不随文件位置变化零维护成本Vue/Vite 生态标准做法(多数 Vue 项目默认配 `@` → src别名仅影响构建时解析运行时无开销。
- **影响范围**9 个源文件(import 侧) + 1 个配置文件(vite.config.ts)。
- **状态**:✅ 已落地commit d6eb855 + 6254d06
## 状态持久化
### 窗口位置/大小:用 tauri-plugin-window-state纯 Rust 层)
- **决策**:窗口位置/大小/最大化用 `tauri-plugin-window-state` 插件Rust 层自动接管),而非前端 localStorage + setPosition/restore 方案。
- **原因**:插件自动覆盖主窗口 + 动态创建的 `ai-detached` 子窗口,零前端代码、零竞态;前端方案需手动同步且对子窗口生命周期处理复杂。需配套 `window-state:default` capability 权限。
- **状态**:✅ Sprint 10
### detached/docked 不持久化
- **决策**UI 布局持久化,但 `detached`/`docked` 重启后强制回 `false`,不随 `df-ai-ui` 落盘。
- **原因**:重启后分离窗口必然不存在,若恢复为 `true` 会让 UI 状态指向不存在的窗口按钮失灵、panelOpen 错乱)。这两个态是运行时临时态,不属可恢复布局。
- **状态**:✅ Sprint 10
> UI 布局 localStorage + 模块级恢复的细节见 [归档文档](./功能决策记录-归档-2026-06-14.md)。
### 消息列表虚拟滚动:自研 → 彻底移除 [2026-06-17 → 2026-06-18]
- **决策**AI Chat 消息列表虚拟滚动选**自研方案**IntersectionObserver + sentinel + ResizeObserver仅渲染层裁剪**→ 2026-06-18 彻底移除**(删 useAiVirtualScroll.ts + AiChat.vue 移除全链路),消息恒渲染。
- **原因/取舍**
- **不选 vue-virtual-scroller**——DynamicScroller 接管滚动容器 DOM + 重排子节点,破坏 `.ai-messages` flex/gap 布局 + onMessagesScroll(isNearBottom/scrollToBottom)/流式滚到底部既有逻辑。
- **自研只做渲染裁剪**原方案——sentinel 占位保 scrollHeightIO mount/unmount 可见区间外消息pinnedKeys 保活流式末条。
- **→ 彻底移除的取舍2026-06-18**:①**IO/RO 时序致重叠(移除主因)**——IO 判可见 + RO 测高度异步回调与 Vue 响应式交织,卸载分支 height=0 时 minHeight fallback 仍有竞态窗口reply1 移出 pinned + bubble 重建时 RO/IO 捕获 height=0 → slot 塌 0 → 后续上移重叠);多次修 fallback8abcd56+ 禁用裁剪0ca5d98验证重叠仍偶发shouldRender 恒 true 时 IO 仍设/清 sentinel inline minHeight 竞态源未除。②**消息量级不需要**——单会话几十条,恒渲染无性能问题。③**简化优于优化**——删 175 行 composable + AiChat 5 处调用,消除时序竞态源。
- **状态**:✅ 2026-06-17 落地e38474b→ ❎ 2026-06-18 彻底移除(工作区待提交:删 useAiVirtualScroll.ts + AiChat.vue 移除 import/解构/setupVirtualScroll/watch lastStreamingRenderKey/template :ref+shouldRenderMsg 条件)
## 应用启动 / 数据库配置
### Dev 与 Build 拆分独立数据库 [2026-06-13]
- **决策**`lib.rs` 启动时按 `cfg!(debug_assertions)` 选 DB 文件名——debugDev 模式)用 `devflow-dev.db`releaseBuild 模式)用 `devflow.db`,两库同处 `app_data_dir()``top.1216.devflow`)下,靠文件名区分。
- **原因/取舍**原启动代码无编译模式分支Dev 与 Build 共用一个 `devflow.db`——Dev 频繁改动/清空会污染 Build 侧真实运行数据。拆分后 Dev 库可随意折腾Build 库长期保留作运行效果基线。**文件名区分而非子目录**——改动最小lib.rs 一行 if两库平铺同目录便于备份/查看。**现有 `devflow.db` 文件名未变归 Build**零迁移零数据丢失Dev 首次启动自动建空库。
- **边界**`app_data_dir()``tauri.conf.json``identifier` 决定、与编译模式无关故拆分前两种模式确读同一文件。docs/使用手册备份命令 `cp devflow.db` 仍正确(备份 Build 真实数据)。
- **状态**:✅ 2026-06-13 落地(`lib.rs:24`
## UI 反馈与弹层
### toast/confirm 自建,不引 Arco / 不用 window.confirm [Sprint 10]
- **决策**Settings 页轻量提示与删除确认用自建 `toast`(顶部 fixed3s 自动消失)+ `confirmDialog`(遮罩 + 卡片Promise 化),而非引入 Arco Message/Modal 或原生 `window.confirm`
- **原因**:① `@arco-design/web-vue` 虽在依赖但 `main.ts``app.use` 注册,引 Message/Modal 要补全局注册 + 样式加载,过重违反做减法;② `window.confirm` 在 Tauri webview2 带「来自 localhost:端口」来源信息,无法去除,体验差。自建零依赖、样式可控(主题色)、`await confirmDialog()` 语义贴近原生 confirm。
- **演进** [2026-06-13]AiChat 删对话需确认 → 第二处复用落地。抽成 `src/components/ConfirmDialog.vue``visible`/`msg`/`dangerLabel` props + `@result` emit。按钮样式内联自包含不依赖外部 `.btn-*`——因 Settings 是 `scoped`,组件拿不到其内定义的 `.btn-danger`。选 SFC 组件而非 `useConfirm()` composable模板/遮罩/Transition 动画/CSS 才是真正重复主体Promise 封装留在调用方(~8 行)。
- **状态**:✅ Sprint 10
## 技能 / 联想
### 首批 Claude 3 类 + path 去重
- **决策**:技能联想首批数据源 = Claude skills / commands / plugins 三类SKILL.md frontmatter按 path 去重(`cache/``marketplaces/` 重复)。
- **原因**frontmatter 格式统一(`name`/`description`/`user_invocable`可统一解析Codex frontmatter 一致后续可扩展openclaw 属 agent 选择层不纳入。
- **状态**:✅ Sprint 8待实测
## 决策治理产品化评估2026-06-13
> 审视 DevFlow 是否应把决策治理(记录/锚点/完成度/自检/漂移)做成产品功能。机制设计详见 [规格契约自检机制-2026-06-14.md](../专项设计/规格契约自检机制-2026-06-14.md)。
### 5 痛点产品内未覆盖,真实运转的寄生 Claude Code 层 [2026-06-13]
- **决策**DevFlow 产品内对决策治理 5 痛点「设计满格、代码两极」——df-traceability 死代码(无表/无 IPC/无前端);契约锚点/AI 自检/漂移检测=规格契约自检机制-2026-06-14.md 纯设计稿0 行代码);完成度无聚合视图。唯一真实运转的(功能决策记录-2026-06-14.md + decision-record skill + dr-check hook寄生在 Claude Code 协作层,未沉淀进产品。
- **原因/取舍**:没用 Claude Code 的用户DevFlow 给不了任何决策治理能力。这套能力寄生在协作工具上,核心价值未进产品。
- **状态**:📐 待产品定位决策
### df-traceability 是锚点雏形Sprint 2 被砍(死代码可复活)[2026-06-13]
- **决策**`crates/df-traceability/``Annotation.location`(文件路径+行号)= 规格契约自检机制设计的「代码锚点」雏形,`Decision` struct 精确对应决策记录痛点。但 Sprint 2 对抗论证时被砍/降级,此后无表、无 IPCquery 方法全 `vec![]`
- **原因/取舍**:非显然关联——文档层设计的活契约+锚点机制,本质是产品外部用更轻方式重发明被砍的 df-traceability 轮子。是否复活取决于产品定位抉择Sprint 2 砍的理由(优先级低/过度设计)现需重新评估。
- **状态**:📐 待评估
### 产品化推荐路径 C 混合,完成度驾驶舱起步 [2026-06-13]
- **决策**三路径——A 全产品化(复活 df-traceability 全栈+spec 自检 AI成本大/重蹈 Sprint 2 覆辙风险B 纯寄生(承认是 Claude Code 协作层,只优化 skill/hook产品核心价值存疑**C 混合推荐——产品做数据底座decisions 表+完成度聚合+视图AI 验证/漂移留协作层**。最小起步:只做完成度驾驶舱(痛点 3
- **原因/取舍**C 分离「确定的数据层」与「不确定的智能层」,先做确定的低风险项。完成度驾驶舱起步:①最痛(记不住做了/没做②技术已存在tasks/ideas 有 status缺聚合 IPC+Dashboard 视图)③立刻可见④验证真会用再扩(避免 Sprint 2 式膨胀)。
- **状态**:📐 待用户拍板DevFlow 要否成为「决策治理/完成度驾驶舱」产品)
## crate 治理 / 模块结构2026-06-14
> 跨 crate 的删留与拆分决策。涉及 df-evolve 领域保留决策的推翻、coordinator 空壳的去留、ai.rs god file 的拆分方式。
### 删除 5 个零引用 crate推翻 df-evolve 领域保留决策)[2026-06-14]
- **决策**:整删 5 个 crate——`df-evolve` / `df-plugin` / `df-stages` / `df-task` / `df-traceability`。**推翻既有「df-evolve 领域类型保留」决策**:连同 `Knowledge`/`PromptTemplate`/`ReviewRule` 领域类型一起整删,知识库领域统一走 `df_storage::models::KnowledgeRecord`,不再维护独立领域模型层。
- **原因/取舍**
- **零引用铁证**:全仓跨 crate 引用为 0——`src-tauri/src` 下 0 处 `use`,其他 crate `Cargo.toml` 不依赖,仅 `src-tauri/Cargo.toml` 声明 `df-evolve` 但源码零用。整坨孤立骨架。
- **推翻归档决策**`功能决策记录-归档-2026-06-14.md:355` 原记「`Knowledge`/`PromptTemplate`/`ReviewRule` 领域类型有意保留待 Tier 1 Service 复用」——本次决定**放弃 Tier 1 独立领域模型路线**。理由:① 知识库 Tier 1 已在 `KnowledgeRecord`df-storage上落地 LIKE + 向量混合检索 + 状态机,实际运转的领域模型就是 `KnowledgeRecord`df-evolve 的领域类型成为「理想但悬空」的另一套定义,重复且误导;② 维护两套领域类型是「未来可能复用」的预期成本 vs 「现在重复定义 + 误导性地雷」的实际危害,用户选消灭后者。
- **df-task::Task 一并删**`Task`(含 `branch_id`/`tags`/`estimate_hours` 等比 `TaskRecord` 更丰富的字段)作为「理想任务领域模型」长期悬空零引用,任务领域统一用 `df_storage::models::TaskRecord`
- **df-traceability 整删**:原被记为「锚点雏形可复活」(见上方「决策治理产品化评估」)。本次决定删除——如未来真需锚点机制,重新评估而非保留死码。死码「可复活」是一种伪期权,实际价值是误导后续维护者以为它在运转。
- **df-plugin / df-stages 属过早设计**df-pluginWASM/动态库、df-stages11 阶段节点 `execute()` 全空壳未启动即删与「人机协同设计基准」中「AI 反复读写的代码不养空壳」一致。
- **影响**:① `src-tauri/Cargo.toml` 需移除 `df-evolve` 依赖声明;② 「决策治理产品化评估」中 df-traceability 相关条目(锚点雏形 / 完成度驾驶舱数据底座)状态需重新标注为「无现存代码可复用」;③ 知识库功能域(`## 知识库`)所有决策继续适用,但实现载体明确为 `KnowledgeRecord` 而非 df-evolve 领域类型。
- **状态**:✅ 2026-06-14 落地(用户拍板删 5 crate + 领域类型)
### coordinator.rs 保留B 路线占位空壳,不删)[2026-06-14]
- **决策**`crates/df-ai/src/coordinator.rs``AgentCoordinator`(多 agent 协作占位空壳,`run()` 返回硬编码 TODO**保留不删**加注释标注「B 路线待立项」。
- **原因/取舍**:与 aichat 升级 A/B 路线拆分一致——A 路线先做 UX 快赢已进行B 路线单独立项补多 agent 协作决策能力。coordinator 是 B 路线的入口锚点,**有意保留占位**而非删除:① B 路线立项时有现成挂载点trait + 结构骨架),不必从零设计;② 与上述 5 crate 删除不矛盾——5 crate 是「无路线图占位的纯死码」coordinator 是「有明确后续路线B 路线)的占位」,二者判断标准不同。区别在「是否绑定明确的演进路线」。
- **状态**:📐 B 路线待立项(保留空壳 + 加注释)
### ai.rs 拆分:低风险子 module 而非下沉 crate [2026-06-14]
- **决策**`src-tauri/src/commands/ai.rs`2663 行 god file拆成 `commands/ai/` 子 module 目录11 个文件mod + commands + agentic + stream_recv + conversation + title + audit + skills + prompt + tool_registry + knowledge_inject`pub use` 保持 `state.rs`/`lib.rs`/`knowledge.rs` 引用路径零改动。**采用低风险子 module 方案而非下沉到 df-ai crate**。
- **原因/取舍**:① 2663 行单文件消耗大量 AI context每次理解靠结构与「人机协同设计基准」中「组件拆分从『不做』升为『值得做』」一致。② **选子 module 不选下沉 crate**——下沉到 df-ai 需动 crate 依赖图df-ai 反向依赖 src-tauri 的 state/types引入循环依赖风险 + 跨 crate 重构成本;子 module 仅在同 crate 内切目录,`pub use` 保持对外 API 不变,零引用路径改动,重构面最小。③ 子 module 仍能达成「职责单一」的 AI 可读性目标,与下沉 crate 收益相当但风险低一个数量级。
- **实施验证2026-06-14 落地)**
- 11 文件合计 2833 行,最大 knowledge_inject.rs 557 行含测试commands.rs 529 行17 个 IPC 命令),其余均 < 400 行。
- 路径契约全保:`commands::ai::{AiSession, build_ai_tool_registry, restore_pending_approvals, spawn_embedding_for_knowledge, trigger_extraction_now}` + 17 个 invoke 命令state.rs/lib.rs/knowledge.rs/commands/mod.rs **零改动**
- `cargo check` 0 error`cargo test ai::` 19 passed。剩 3 warning 全为预存非本次引入。
- 顺带修 bug`ai_conversation_list``models` 字段从 JSON 字符串直塞改为解析为 `Vec<String>` 数组下发(前端期望数组,原代码传字符串)。
- **状态**:✅ 2026-06-14 落地11 子 module + glob 重导出 + models bug 修复cargo check/test 通过)
### 前端 ai.ts 拆分路线Astore 留 state 单例 + composable不引入 Pinia [2026-06-14]
- **决策**`src/stores/ai.ts`758 行 god store拆成 store 骨架(留 reactive state 单例 + 模块级私有变量)+ `src/composables/ai/` 下 6 个 composable`useAiEvents`/`useAiStream`/`useAiSend`/`useAiConversations`/`useAiWindow`/`useAiPanel`)。`useAiStore()` 统一入口展开所有 composable 方法,**返回 shape 不变,组件零改动**。
- **原因/取舍**
1. **与项目既有 store 风格一致**——project/knowledge/settings 全是「手写 reactive + 工厂函数」模式,引入 Pinia 会破坏一致性、增加心智负担。
2. **组件零改动**——AiChat.vue/AiDetached.vue 等用 `const { state, sendMessage } = useAiStore()` 解构,保持 `useAiStore` 返回 shape 不变即可。
3. **选路线 Acomposable 挪逻辑、store 留 state而非路线 BPinia 多 store**——后者要改 19 个 state 字段归属和所有组件 import风险远大于收益。
4. **与后端 ai.rs 子 module 拆分配对**——前后端 god file/god store 同步拆解采用各自生态的惯用拆法Rust 子 module / Vue composable不强求统一模式。
- **影响**:确立项目 store 架构约定(手写 reactive + composable不用 Pinia影响所有未来 store 设计。
- **状态**:🚧 执行中workflow w20yb6n6b 编排vue-tsc 自验证)
### AI trait 下沉:拆 df-ai-core 轻层,确立全局 AI 接入标准 [2026-06-14 📐]
- **决策**:将 `LlmProvider` trait + AI 数据类型(`ChatMessage` / `CompletionRequest` / `ToolDefinition` 等)从 `df-ai` 拆出,下沉到新轻量 crate `df-ai-core`(零 http 依赖);`df-ai` 保留为「实现 + `ModelRouter` + provider 工厂」hub持有 `reqwest`/`openai_compat`/`anthropic_compat`);所有消费 crate`df-ideas` 接对抗评估、未来 `df-knowledge`/`df-workflow` 等)**只依赖 `df-ai-core` 的 trait不直接依赖 `df-ai`**;真实 provider 由 `src-tauri` 最上层装配注入。**F-260614-03 及后续所有 AI 接入点统一照此,不再 per-module 自定义 trait。**
- **原因/取舍**(纯 ai-coding 工作模式下重算 F-03 原 A/B/C 选型):
1. **砍「人审查的契约面」而非「打字量」**——一份全局 trait = 一份契约给人过目 + 规格契约 self-check 锚一点per-module 自定义 trait原 A= N 份发散契约,审查负担与漂移风险同涨。纯 ai-coding 下接线/mock 全由 AI 吸收,故 A 的「适配器成本」、B 的「mock 成本」论点作废。
2. **保住纯逻辑 crate 零摩擦自检**——`df-ideas`/`df-storage` 只依赖 trait 不拖 `reqwest`,规格契约机制 A/B 测试自动跑无 http 依赖;裸 Bdf-ideas 直接依赖 df-ai会污染纯逻辑 crate、自检摩擦上升。
3. **与现有 roadmap 对齐**——`ModelRouter`F-260614-01、多 Provider 负载均衡池F-260614-04、provider 工厂(已落地)本就在 df-ai 内走「gateway」方向消费方选型应顺此而非另起 N 个 trait。
4. **无环且与 ai.rs 拆分不冲突**`df-ai-core`trait+类型)← `df-ai`(impl) / `df-ideas`(use trait)`src-tauri` 装配。「ai.rs 子 module 不下沉 crate」规避的是 src-tauri→df-ai 反向依赖;本决策是 df-ai→df-ai-core 正向拆分,方向相反、互不矛盾。
- **否决项**:纯 Aper-module trait= 全局 N 份发散契约,反模式;裸 Bdf-ideas 直接依赖 df-ai= 纯逻辑 crate 被 reqwest 污染、自检摩擦上升。
- **退路**:若不愿加新 cratetrait 可放 `df-core`语义稍糙——LLM 非领域类型,但零新 crate可接受
- **状态**:📐 设计定稿2026-06-144 项决策已定:① 拆分边界=仅 trait+数据结构 ② provider 注入=构造注入 `Engine::new(Option<Arc<dyn LlmProvider>>)` ③ LLM 失败=自动降级启发式+warn+`EvaluatedBy` 标记 ④ provider 构造=应用层 src-tauri 装配注入),未实施。详见 [F-07-df-ai-core-trait下沉设计-2026-06-14.md](../已编号方案/F-07-df-ai-core-trait下沉设计-2026-06-14.md)。
### df-core → df-types 改名:类型库非核心,语义明示「类型契约层」[2026-06-17]
- **决策**crate `df-core` 改名为 `df-types`。全 workspace 机械改名54 处源码引用 + git mv + 9 个 Cargo.toml 依赖声明 + Cargo.lock 自动迁移)。
- **原因/取舍**
- **「core」名称误导**——df-core 含 0 业务逻辑、0 内部依赖、0 宏自引用,纯粹是跨 crate 共享的类型定义ProjectRecord / TaskRecord / IdeaRecord / KnowledgeRecord / ChatMessage 等)+ re-export 聚合。叫「core」暗示它是核心业务层实际是「类型契约层」。
- **改名收益 > 成本**——54 处机械替换纯字符串无语义改动git mv 保历史Cargo.lock 跟随 Cargo.toml 自动更新。一次性 10 分钟操作,消除后续所有新贡献者的认知摩擦。
- **不影响 df-ai-core**——df-ai-core 是 AI trait 层LlmProvider / CompletionRequestdf-types 是领域模型层ProjectRecord 等存储实体),两者职责清晰不重叠。
- **状态**:✅ 已落地commit 4be1591
## 工作流人工审批节点B-03
### HumanNode 审批响应机制subscribe→send→select! 广播过滤等待 [2026-06-14 📐]
- **决策**df-workflow `HumanNode.execute` 改为「先 `subscribe()` → 发 `HumanApprovalRequest``tokio::select!` 循环等 `HumanApprovalResponse`」,按 `execution_id + node_id` 双键过滤命中后返回 NodeOutput`select!` 三分支 = 响应 / 超时(配置 `timeout_secs` 默认 3600s/ 取消500ms 轮询 `is_cancelled`)。复用既有 `EventBus`(broadcast) / `HumanApprovalResponse` 事件 / `approve_human_approval` IPC / 前端 store——零新增基础设施仅改 HumanNode 一处。
- **原因/取舍**
1. **订阅时序铁律**tokio broadcast 不回放历史,必须 `subscribe()` 先于 `send(Request)`,否则 receiver 错过 Response 死等超时。
2. **双键过滤**node_id 单键不够跨工作流可能重复、execution_id 单键不够(同层多 HumanNode双键才完备。
3. **Lagged 容忍**capacity 256 + 审批低频,漏自身 Response 概率极低;`continue` 优于丢弃(丢弃误判超时更糟)。
4. **decision 强制校验**options 非空时强制 `decision ∈ options`非法值报错而非静默放行——审批门控不能被脏输入绕过options 空时允许自由文本。
5. **B-06/B-07 是并发隔离/取消的前置,非单流功能前置**:单工作流 B-03 照常工作;并发安全等 B-06execution_id 下沉);取消机制需 B-07 + `set_cancelled` + cancel IPC单列 **B-03b**。B-03a响应等待 + 超时)不依赖 B-07。
- **边界**:取消分支在 B-07 + `set_cancelled` 补齐前恒 false等价无取消功能不残跨工作流并发 HumanNode 在 B-06 修前有 Response 错配风险。
- **状态**:📐 设计完成2026-06-14未实施。详见 [B-03-人工审批响应机制-2026-06-14.md](../已编号方案/B-03-人工审批响应机制-2026-06-14.md)。
## 任务推进链7 态状态机 + 工作流联动)
> tasks 表从 todo→done 的状态推进链路。阶段17 态状态机 + advance_task CAS 原子写 + 软删除已落地阶段2工作流联动task_id + 完成回调 advance_task + DAG 模板)进行中。详细实施路径见 [任务推进链实施路径-2026-06-16.md](../专项设计/任务推进链实施路径-2026-06-16.md) / [推进链阶段2实施路径-2026-06-16.md](../专项设计/推进链阶段2实施路径-2026-06-16.md)。关联决策 D-260616-01~04前端7态对齐 / 软删除 / Node trait 归属 / 阶段1先行
### advance_task / 状态机走 df-nodes Node trait不复活 df-taskD-260616-03
- **决策**:任务推进业务逻辑(`can_transition_to` 状态机 / `advance_task` 原子写 / 闸门节点)落在 **df-nodes crate 的 Node trait 扩展**`task_state_machine.rs` / `task_advance_node.rs`IPC 层 thin 入口。不复活 2026-06-12 刚因零引用删除cf017f8的 df-task crate不塞 commands/task.rs。
- **原因/取舍**:① 对齐 D3「业务逻辑在 df-nodes 实现Node trait 纯接口df-workflow/src/node.rs:67」原则② 复活一个零引用刚删的 crate 是制造新死码df-nodes 补 `df-storage` 依赖读 TaskRecord 即可(核实无循环依赖);③ IPC 层保持 thintask.rs 仅 3 行转发)守住 D3 不让业务逻辑下沉 IPC。前端对齐后端 7 态D-260616-01types.rs:131激活 InReview/Testing/Blocked 三闸门态)。
- **状态**:✅ 阶段1 落地commit d2cb38c7态状态机 + advance_task CAS + 软删除25 测试);🚧 阶段2 进行中batch32注册 TaskAdvanceNode + config 下沉 + HumanNode reject
### DagExecutor config 下沉:节点级覆盖全局级 deep_merge④-1
- **决策**`DagExecutor.run` 构造 `NodeContext.config` 从「`initial_config.clone()` 覆盖一切」改为「`deep_merge(node_def_config, initial_config)`」——节点级配置覆盖全局级节点定义优先。Dag 加 `node_configs: HashMap<NodeId, Value>`build_dag 填入 NodeDef.configrun 合并下沉。
- **原因/取舍**原实现executor.rs:99-107用全局 initial_config 覆盖 NodeDef.config**节点级配置被完全忽略**——TaskAdvanceNode.execute 读 `ctx.config.task_id` 拿到全局 config 而非节点定义写的 task_id节点参数化失效。这是阶段2 的架构前置阻塞点:不修则 TaskAdvanceNode 无法从 DAG 接收 task_id。选「节点级覆盖全局级」节点定义优先而非全局覆盖节点级因节点是更具体的配置源。deep_merge 对 Object 递归合并,非 Object 节点级直接覆盖。现有 HumanNode/AiNode 也受益(它们当前读 ctx.config 拿全局 config但无人通过 NodeDef.config 定义节点参数故未暴露)。
- **状态**:🚧 实施中batch32 ④-1 agentdag.rs + executor.rs + registry.rs + workflow.rs配 deep_merge / node_config_overrides 测试)。
### DAG 模板硬编码,不建 workflow_defs 表(②-6
- **决策**:任务推进的工作流 DAG 模板todo→in_progress / in_review→testing / testing→done 三条推进边 + 退回)**硬编码**在 `df-nodes/task_workflow_templates.rs`(导出 `template_for(target_status) -> DagDef`**不建 workflow_defs 表**。`tasks.workflow_def_id` 字段留 None。
- **原因/取舍**模板数量少且稳定3 条推进边 + 退回),建表需 CRUD UI + 版本管理 + 关联维护,过度工程。[业务系统设计-2026-06-12.md](./业务系统设计-2026-06-12.md) 确认 workflow_defs 从未建表(工作流定义 dag_json 内嵌 workflow_executions延续此约定。硬编码模板随代码版本管理零运行时配置开销。
- **状态**:📐 设计定稿待实施(②-6batch33+)。
### 工作流回调语义:成功与任务推进解耦,失败按 target 退回(②-3/②-4/②-5
- **决策**:工作流完成后回调 advance_task 的语义——**成功**executor Oktask_id + target_status 都 Some 时调 `advance_task_atomic`,回调失败只 warn 不回滚工作流(工作流已完成是事实,任务推进失败前端提示手动处理);**失败**executor Err按 target_status 推算退回态testing→in_review / in_review→in_progress调 advance或加 `failure_target_status` 参数。HumanNode reject②-5从 Ok 改返 Err使审查拒绝走 failed 触发退回。
- **原因/取舍**工作流成功与任务推进是两个独立事实解耦避免「工作流成功但任务推进失败时回滚已完成工作流」的复杂性失败退回让审查拒绝能回流上一态review_rounds+1。CAS 已防回调与手动 advance 并发撞advance_task_atomic 捕获 InvalidState 降级。跨表事务缺失阶段2 回调失败降级阶段3/4 补 Database.transaction())。
- **状态**:📐 设计定稿待实施batch32 做 ②-5 HumanNode reject 语义化batch33 做 ②-3/②-4 回调)。
### AiNode 自审闸门:内部 return Err 复用 executor first_err方案 A[2026-06-17]
- **决策**AiNode 自审闸门选**方案 AAiNode 内部 return Err**——自检失败时 return Err(SelfReviewFailed) 复用 DagExecutor 已有的 first_err 收敛 + ②-4 回调错误路径。不选方案 BDAG edges 条件 + ConditionEngine依赖暂缓的 T-260614-11和方案 CDagExecutor 核心循环改闸门钩子。gate 配置默认 false 向后兼容阶段2 行为不变testing 模板可设 gate:true 启用。
- **原因/取舍**
- **方案 A 零 executor 核心改动**——Err 沿既有 execute() → run_node() → first_err 路径自然冒泡executor 核心循环零行变更。②-4 回调已处理 failed 分支(退回上一态),闸门失败自动走此路径无需额外代码。
- **方案 B 依赖 ConditionEngine**——T-260614-11 条件表达式引擎尚在 📐 设计阶段,为单个闸门功能拉入未完成的依赖链路风险高。
- **方案 C 改 executor 核心循环**——在 node 执行前后插钩子before/after execute是通用扩展点但当前仅 AiNode 一个消费者,为单一场景改核心循环过度工程;且钩子语义(是否中断后续节点、是否影响 DAG 继续执行)需详细设计,复杂度远超方案 A 的 1 行 return Err。
- **状态**:✅ 已落地commit e16d038
## 需求与待办
> 汇集散落于各决策条目状态(📐/🚧)的待办 + 新增需求细节 + 需求澄清。单一清单,避免遗漏。
### 📋 待做需求
| 需求 | 功能域 | 来源 | 优先级 |
|---|---|---|---|
| Sprint 9/10 多项编译过未 tauri dev 实测(评分 IPC 缩放 / update_full / promote_idea / Store getter | 灵感/立项/Store | Sprint 910 🚧 | P1 |
| 切对话不中断路由:部分场景运行时实测 | AI Chat 可靠性 | Sprint 8 🚧 | P1 |
| 技能联想「使用」:首批 3 类联想已做,联想后实际触发/执行技能未实现 | 技能/联想 | Sprint 8 | P2 |
| 灵感对抗评估接 LLM论点/evidence 由 df-ai LlmProvider 生成(现启发式 fallback | 灵感模块 | Sprint 9 📐 | P2 |
| 知识库 Tier 1AI Chat ↔ 知识库双向闭环(沉淀+检索注入+reuse_count+审核收件箱+状态机+provenance 溯源+克制检索,无人工评分) | 知识库 | 2026-06-13 ✅ 已实施(Sprint 15) | P1 |
| 路径校验根治workspace 白名单 + canonicalize现仅拒 `..` + 敏感目录) | 工具调用 | Sprint 6 | P2 |
| 停止生成 idle 即时优化:`tokio::sync::Notify` 替代 120s 轮询 | AI Chat 可靠性 | Sprint 6 | P3 |
| 多 Provider 负载均衡池(备用模型/多账号聚合,全局容量=min(各 provider 上限之和, global_cap) | AI Chat 并发控制 | 2026-06-13 📐 | P2 |
| 裁剪/压缩消息按需召回Query Function + 分层存储: TrimRecord 追踪被移除范围 → DB 全量归档按需检索 → 精准注入 build_for_request触发方式待定:自动/手动/语义检索) | 上下文窗口管理 | 2026-06-13 📐 | P3 |
| ✅ IPC参数驼峰/蛇形不对齐误报澄清Tauri v2 自动将前端 camelCase 参数名转后端 snake_case`approve({toolCallId})` / `setConcurrencyConfig({globalLimit})` 实际正确、功能正常——无需修 | AI Chat | 2026-06-13 审查误报 | — |
| 🔴 df-workflow ConditionEngine 默认 true所有未识别条件表达式均通过工作流条件分支形同虚设`Ok(false)``Err` 一行可修 | 工作流引擎 | 2026-06-13 代码审查 | P0 |
| 🔴 df-workflow DagExecutor execution_id 硬编码 "dummy-execution-id":所有执行 ID 相同,追踪/审计失效 | 工作流引擎 | 2026-06-13 代码审查 | P1 |
| 📐 df-workflow HumanNode 假实现execute 注释"等待审批"但首次迭代直接 return "同意" — **设计完成 [B-03-人工审批响应机制-2026-06-14.md](../已编号方案/B-03-人工审批响应机制-2026-06-14.md),待实施**(依赖 B-06 并发隔离 / B-07 取消前置) | 工作流引擎 | 2026-06-13 多代理探索 → 2026-06-14 设计 | P0 |
| 🔴 df-workflow NodeRegistry::default() 的 script 工厂 unimplemented! panic用 default() 构建注册表 + 跑 script 节点即崩溃进程(非优雅 Err | 工作流引擎 | 2026-06-13 多代理探索 | P0 |
| 🔴 df-workflow executor 每节点拿全新空 StateMachineself.state_machine 从不传入 NodeContextHumanNode is_cancelled 恒 false取消机制失效 | 工作流引擎 | 2026-06-13 多代理探索 | P1 |
| 🟡 promote_idea 两步写非事务INSERT project 成功后若 UPDATE idea 失败,项目存在但想法状态未变,补偿删除可修 | 灵感/立项 | 2026-06-13 代码审查 | P1 |
| 🔴 分离窗口detached跨窗口状态失效用 localStorage 传递生成态快照df-ai-gen/textTauri 多 webview 不共享 localStorage 致静默失效;用户点 X 关闭(非 closeDetachedWindow后主窗口 `detached` 永真卡死reattachPanel 死代码未接线)。需改 Tauri 全局 emit/listen 同步 + 窗口销毁事件复位 | AI Chat 分离窗口 | 2026-06-13 代码审查 | P1 |
| 模型能力声明与自动路由系统 Phase 1ModelCapability 数据模型 + ModelRouter 重写 + 7 调用点接入 + Settings 模型池编辑 UI + AiChat 模型下拉。核心:按任务需求(模态/功能/成本)自动匹配合适模型,不再所有场景共用 default_model | 模型能力与路由 | 2026-06-13 📐 设计完成 | P1 |
| 模型能力系统 Phase 2多模态消息支持——ChatMessage.content: String → Vec<ContentPart>(Text/Image);前端粘贴/拖拽图片vision 模型自动路由 | 模型能力与路由 | 2026-06-13 📐 | P2 |
| 模型能力系统 Phase 3Agent 内智能路由——Agentic Loop 每轮按子任务构造不同 TaskRequirements成本预算控制模型级联降级跨 Provider 搜索 | 模型能力与路由 | 2026-06-13 📐 | P3 |
| 📋 已澄清「显示多开」= 多会话来回切可对话(非 AI Chat 窗口多开)[2026-06-17]:用户原意是「多个会话之间切换都可继续对话」(当前切换会话后旧会话生成态丢失)。关联 F-09 多会话架构决策——A 路线(单例 AiSession + 软隔离Sprint 8 已落地切对话不中断路由待实测验证是否满足需求T-260614-02B 路线真多会话AiSession 单例→多实例)是备选但触及 memory 记录的「AiSession 单例未动」架构约束。→ 2026-06-17 澄清为「多会话并发」需求,推荐先实测 A 路线再定是否需 B 路线。已记 todo L652 + 待决策.md🟡 A/B 路线决策) | AI Chat / 多会话 | 2026-06-13 待澄清 → 2026-06-17 已澄清 | 🟡 待 A/B 路线决策 |
| 🔴 待审批持久化根治(重启恢复)未生效——两处逻辑断裂致恢复链路跑不通:① `ai_conversation_switch` 无条件 `pending_approvals.clear()` 清空 `restore_pending_approvals`(init)重建的内存 HashMap`ai_pending_tool_calls`/`ai_approve` 均依赖内存态 → 重启后前端 `switchConversation` 触发 clear → 审批卡片查空永不显示、审批报"未找到挂起的审批";② `ai_approve` 的 recovered 守卫跳过 `save_conversation`(注释称"防空 messages 污染老对话"前提不成立——switch 时 `restore_from_messages` 已载完整历史,审批时 messages 非空 → 执行的工具结果不落库,重启后 toolCard 显示 completed 但 result 仍是占位"需要用户审批,等待确认"。修复方向pending 恢复链路改查 DB`ai_tool_executions` WHERE status='pending' 持久化真相源)绕过内存 clear`ai_approve` 内存 miss 时 fallback DB 单条重建再执行recovered 审批通过后正常 save。可顺带删 `restore_pending_approvals`DB 即真相源)。阻断用户"功能逻辑层面解决"诉求——现"根治"实为表面修复 | AI Chat 审批持久化 | 2026-06-13 /review 审查①② | P0 |
| 📋 审批可见性缺口pendingApprovals 无兜底渲染→卡死 [2026-06-13]AI 发起 Med/High 工具审批AiApprovalRequired后暂停等审批不发 delta前端审批唯一出口是 ToolCard 的 pending_approval 内联卡片(靠 findToolCall 置 tc.status但 state.pendingApprovals 数组有数据却零渲染AiChat.vue 仅 @approve 转发,无 pendingApprovals 模板)。若 tc 卡片未显示审批,用户看不到审批按钮 → AI 永久等 → 文字停卡死。待修A. AiChat.vue 加 pendingApprovals 醒目渲染(顶部条/浮层)兜底审批可见性;或 B. 运行时确认 tc 卡片是否渲染。配套watchdog 在 AiApprovalRequired 暂停,审批没弹则 watchdog 盲点,需加"审批超时未响应"提示 | AI Chat 审批 | 2026-06-13 | 📋 A/B 待定 |
| 📋 node_executions 全表 list当前只写不读若未来前端要看某次工作流执行的节点明细需**新增** `list_node_executions(execution_id)` 命令 | 工作流引擎 | 2026-06-13 代码审查 | 📐 待需求驱动 |
### 📋 需求澄清
- **「决策」术语边界**2026-06-12devflow 语境「决策/决策需求点」= 日常开发功能细节取舍(为什么这么定),**非** aichat 决策能力升级B 路线 coordinator/conditions。后者属架构层记 Phase2计划/模块文档,不混入本文档。
- **代码审查甄别原则**2026-06-13审查发现问题时按「运行时失败/数据损坏 → 简单清理 → 记录不动 → 不做」四档甄别。当前项目规模下list_all 无 LIMIT、ALLOWED_COLUMNS 不分表、bool→int 重复等属「记录不动」——个人工具表不超千行,加分页/拆白名单是过度设计,维护成本 >> 收益。原则:**真实 bug 修、简单清理做、规模不到位的优化先不动**,保持全局简洁和扩展容易。
## 文档维护
### 文档历史项保留原则:标状态不删行 [2026-06-15]
- **决策**所有文档ARCHITECTURE.md + 模块文档)中的历史设计项**一律保留原文不删除**,仅在行末或旁注标注实现状态:`✅ 已实现` / `❌ 未实现(设计预留)` / `⚠️ 骨架空壳(有文件但无实质逻辑)` / `~~已删~~`R-PD-X 等重构决策引用)。
- **原因/取舍**:全量核对报告(2026-06-15)发现 ARCHITECTURE.md 含多出虚构/过时项(ModelRouter 已删/Docker 等 5 节点未实现)。初版方案为「删虚构行+注」,用户两次明确否决删方案,要求保留全部历史项+标状态。理由:① 保留设计演进痕迹,接手方可理解"曾经考虑过什么、为什么没做";② 删除会导致核对报告等交叉引用断链;③ 标状态列比删行信息量更大。
- **影响范围**DOC-260615-01~14 全部文档修项均遵循此原则。已落地ARCHITECTURE.md §5.4 ModelRouter 标 `❌ ~~已删~~` + §5.5 8 节点标 `✅` / `❌`commit be38a44
- **状态**:✅ 2026-06-15 落地(用户两次否决删方案后定稿,首批标注已 commit
**相关文档**
- [Phase 1 架构决策](./Phase1架构决策-2026-06-12.md) — 架构级决策ADR
- [经验记录](./经验记录-2026-06-14.md) — 踩坑/约定/技巧/bug 排查教训
- [功能决策记录-归档](./功能决策记录-归档-2026-06-14.md) — 纯流水/老 Sprint/UX 微调/已被取代
- `PROGRESS.md` — 各 Sprint 工作流水与遗留
- [Phase 2 计划](../07-项目管理/Phase2计划-2026-06-12.md)