diff --git a/docs/03-模块文档/df-storage-存储层-2026-06-12.md b/docs/03-模块文档/df-storage-存储层-2026-06-12.md
index 59230bf..da4d65b 100644
--- a/docs/03-模块文档/df-storage-存储层-2026-06-12.md
+++ b/docs/03-模块文档/df-storage-存储层-2026-06-12.md
@@ -1,12 +1,12 @@
# df-storage 存储层
-> 创建: 2026-06-10 | 最后更新: 2026-06-14
+> 创建: 2026-06-10 | 最后更新: 2026-06-15
---
## 概述
-df-storage 是 DevFlow 的数据持久化层,基于 SQLite (rusqlite),负责连接管理、Schema 迁移(V1-V8)和 CRUD 操作。全部 Repo 由 `impl_repo!` 宏自动生成。
+df-storage 是 DevFlow 的数据持久化层,基于 SQLite (rusqlite),负责连接管理、Schema 迁移(V1-V13)和 CRUD 操作。绝大多数 Repo 由 `impl_repo!` 宏自动生成,KV 类表(`app_settings`)手写 Repo。
---
@@ -15,27 +15,36 @@ df-storage 是 DevFlow 的数据持久化层,基于 SQLite (rusqlite),负责
| 功能 | 状态 |
|------|------|
| SQLite 连接管理 | ✅ |
-| Schema 迁移 V1-V8 | ✅ |
-| impl_repo! 宏 CRUD | ✅ |
+| Schema 迁移 V1-V13 | ✅ |
+| impl_repo! 宏 CRUD(12 Repo) | ✅ |
+| KV 类手写 Repo(SettingsRepo) | ✅ |
| KnowledgeRepo(含向量)| ✅ Sprint 15 |
+| KnowledgeEventsRepo(生命线审计)| ✅ |
| 事务支持 | ⬜ 按需 |
---
-## 数据表(V1-V8 迁移历史)
+## 数据表(V1-V13 迁移历史)
| 版本 | 新增/变更 |
|------|-----------|
| V1 | ideas / projects / tasks / releases / workflow_executions / node_executions(6 张基础表 + 4 索引)|
| V2 | ideas 加 promoted_to/ai_analysis/scores;tasks 加 workflow_def_id/base_branch;workflow_executions 加 project_id/task_id;新建 branches 表(含 2 索引)|
-| V3 | ai_providers / ai_conversations / ai_tool_executions(AI 功能 3 张表)|
-| V4 | 幂等补列:ai_conversations.archived(PRAGMA 探测,兼容坏库)|
-| V5 | 幂等补列:ai_conversations.prompt_tokens / completion_tokens / model / models(Token 用量)|
-| V6 | 幂等补列:ai_conversations.skill(技能注入)|
+| V3 | 新建 **ai_conversations** 表(archived 列交 v4 幂等补建)|
+| V4 | 幂等补列:ai_conversations.archived(PRAGMA 探测,兼容坏库——历史 v3 写入版本号但 ALTER 未生效,统一交 v4 修复)|
+| V5 | 幂等补列:ai_conversations.prompt_tokens / completion_tokens(Token 用量)|
+| V6 | 幂等补列:ai_conversations.models(对话级多 model 记录)|
| V7 | 新建 **knowledges** 表(Sprint 15)|
| V8 | 幂等补列:knowledges.embedding BLOB(Phase 5.5 向量检索)|
+| V9 | 新建 **ai_providers** + **ai_tool_executions** 表(历史遗漏补建,CREATE TABLE IF NOT EXISTS 幂等)|
+| V10 | 幂等补列:knowledges.reasoning;新建 **knowledge_events** 事件表(追加型审计,支撑生命线视图,含 3 索引)|
+| V11 | 幂等补列:projects.deleted_at(软删回收站)|
+| V12 | 幂等补列:projects.path / projects.stack(项目绑定真实代码目录 + 技术栈)|
+| V13 | 新建 **app_settings** 表(通用应用设置 KV,前端 localStorage 迁移目标)|
-### knowledges 表(V7)
+> **当前共 13 张业务表**:ideas / projects / tasks / releases / workflow_executions / node_executions / branches / ai_conversations / ai_providers / ai_tool_executions / knowledges / knowledge_events / app_settings。另有一张内部表 `schema_version`(仅记录版本号,无业务语义)。
+
+### knowledges 表(V7 建表 + V8/V10 补列)
```sql
CREATE TABLE IF NOT EXISTS knowledges (
@@ -52,6 +61,7 @@ CREATE TABLE IF NOT EXISTS knowledges (
source_ref TEXT,
created_at TEXT NOT NULL, -- 毫秒字符串
updated_at TEXT NOT NULL,
+ reasoning TEXT, -- V10 补列,AI 提炼「为何值得沉淀」依据
embedding BLOB -- V8 补列,f32 little-endian
);
CREATE INDEX IF NOT EXISTS idx_knowledges_status ON knowledges(status);
@@ -67,21 +77,25 @@ CREATE INDEX IF NOT EXISTS idx_knowledges_reuse_count ON knowledges(reuse_count
列名白名单(ALLOWED_COLUMNS)防 SQL 注入,各 Repo 声明各自允许的列。
-### 已注册 Repo 列表
+> KV 类表(`app_settings` 无固定 schema,按 key 读写)不走 `impl_repo!` 宏,由手写 `SettingsRepo` 直接 SQL 操作。
-| Repo | 表 |
-|------|----|
-| IdeaRepo | ideas |
-| ProjectRepo | projects |
-| TaskRepo | tasks |
-| ReleaseRepo | releases |
-| WorkflowRepo | workflow_executions |
-| NodeExecutionRepo | node_executions |
-| BranchRepo | branches |
-| AiProviderRepo | ai_providers |
-| AiConversationRepo | ai_conversations |
-| AiToolExecutionRepo | ai_tool_executions |
-| **KnowledgeRepo** | **knowledges** |
+### 已注册 Repo 列表(12 宏生成 + 1 手写 = 13)
+
+| Repo | 表 | 生成方式 |
+|------|----|---------|
+| IdeaRepo | ideas | impl_repo! |
+| ProjectRepo | projects | impl_repo! |
+| TaskRepo | tasks | impl_repo! |
+| ReleaseRepo | releases | impl_repo! |
+| WorkflowRepo | workflow_executions | impl_repo! |
+| NodeExecutionRepo | node_executions | impl_repo! |
+| BranchRepo | branches | impl_repo! |
+| AiProviderRepo | ai_providers | impl_repo! |
+| AiConversationRepo | ai_conversations | impl_repo! |
+| AiToolExecutionRepo | ai_tool_executions | impl_repo! |
+| **KnowledgeRepo** | **knowledges** | impl_repo!(+ 向量/检索自定义方法)|
+| **KnowledgeEventsRepo** | **knowledge_events** | impl_repo!(+ list_by_knowledge 等自定义方法)|
+| **SettingsRepo** | **app_settings** | 手写(KV 表不走宏)|
---
@@ -111,14 +125,17 @@ fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 // 点积 / (‖a‖‖b‖
## 迁移幂等设计
-迁移机制:`schema_version` 表记录当前版本,`MIGRATION_VERSION` 常量为目标版本,`run()` 按 `current_version < N` 顺序应用各版本。
+迁移机制:`schema_version` 表记录已应用版本,`run()` 读出 `MAX(version)` 作 `current_version`,按 `current_version < N` 顺序应用 V1…V13(每版应用后 `INSERT` 对应版本号)。当前最新目标版本为 **13**。
+
+> 注:早期版本曾用 `MIGRATION_VERSION` 常量,现已无此常量——版本号直接散落在各 `if current_version < N` 分支,由 `schema_version` 表动态跟踪。
### v4 解法:PRAGMA 探测列存在性
关键列补建不依赖版本号,用 `PRAGMA table_info(
)` 探测实际 schema,缺列才 `ALTER TABLE ADD COLUMN`。
- 对新库(列已由建表带入)、老库(DDL 正常生效)、坏库(版本号已写入但 DDL 漏生效)三种情况都安全幂等。
-- V5/V6/V8 均复用此模式。
+- V4/V5/V6/V8/V10/V11/V12 均复用此模式(补列迁移)。
+- V9/V10/V13 用 `CREATE TABLE IF NOT EXISTS` 幂等建表(老库已有表则跳过)。
---
@@ -128,9 +145,9 @@ fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 // 点积 / (‖a‖‖b‖
crates/df-storage/src/
├── lib.rs — 模块入口,导出公共 API
├── db.rs — SQLite 连接管理(Database struct)
-├── migrations.rs — V1-V8 迁移逻辑,MIGRATION_VERSION=8
-├── models.rs — 全部 *Record struct(含 KnowledgeRecord)
-└── crud.rs — impl_repo! 宏 + 全部 Repo(含 KnowledgeRepo)
+├── migrations.rs — V1-V13 迁移逻辑(schema_version 表跟踪版本)
+├── models.rs — 全部 *Record struct(含 KnowledgeRecord / KnowledgeEventRecord)
+└── crud.rs — impl_repo! 宏 + 全部 Repo(含 KnowledgeRepo / KnowledgeEventsRepo / SettingsRepo)
```
---
diff --git a/docs/03-模块文档/df-workflow-工作流引擎-2026-06-12.md b/docs/03-模块文档/df-workflow-工作流引擎-2026-06-12.md
index 7dd1238..778f2db 100644
--- a/docs/03-模块文档/df-workflow-工作流引擎-2026-06-12.md
+++ b/docs/03-模块文档/df-workflow-工作流引擎-2026-06-12.md
@@ -1,6 +1,6 @@
# df-workflow 工作流引擎
-> 创建: 2026-06-10 | 状态: 初稿 | 最后更新: 2026-06-14
+> 创建: 2026-06-10 | 状态: 初稿 | 最后更新: 2026-06-15
---
@@ -19,7 +19,7 @@ df-workflow 是 DevFlow 的核心引擎,负责 DAG 定义、拓扑排序、节
| Node trait 定义 | ✅ 已实现 |
| 状态机 (WorkflowRunStatus) | ✅ 已实现 |
| EventBus (broadcast) | ✅ 已实现 |
-| 条件表达式引擎 | ⚡ 仅支持 true/false |
+| 条件表达式引擎 | ⚡ 仅支持 true/false(未识别默认 **false** 保守拒绝) |
| 断点续跑 | ⬜ 待实施(引擎整体无暂停/恢复/快照机制,缺乏底层基础设施支撑) |
## 核心设计
@@ -78,7 +78,7 @@ pub trait Node: Send + Sync {
| `is_registered` | `(&self, type_name: &str) -> bool` | 检查类型是否已注册 |
| `registered_types` | `(&self) -> Vec<&str>` | 列出所有已注册类型名 |
-> `Default` 实现仅注册占位 `script` 工厂(`unimplemented!`),实际 `ScriptNode` 由 `df-nodes` crate 注册。
+> **不实现 `Default`**:原 `Default` 注册了一个会 panic 的 `script` 工厂(`unimplemented!`),违反项目铁律「无 panic——所有占位代码返回空/默认值」,已删除。所有调用方应显式 `new()` + 手动 `register` 真实节点(如 `state.rs::build_registry` 的做法),实际 `ScriptNode`/`AiNode`/`HumanNode` 由 `df-nodes` crate 注册。
### DAG 执行流程
@@ -135,10 +135,9 @@ pub trait Node: Send + Sync {
| `send` | `(&self, WorkflowEvent) -> ()` | 广播事件(忽略接收者已关闭错误,异步) |
| `subscribe` | `(&self) -> broadcast::Receiver` | 订阅事件流 |
| `emit_human_approval_request` | `(&self, WorkflowEvent) -> Result` | 发送人工审批请求(返回接收者计数) |
-| `try_recv_human_approval` | `(&self, execution_id, node_id) -> Option` | **TODO 占位**:当前恒返回 `None`,审批响应存储/检索未实现,需配合前端 |
| `Default` / `Clone` | — | `Default` 走 `new`;`Clone` 复刻 `sender`(broadcast sender 可 clone,共享通道) |
-> `emit_human_approval_request` 与 `try_recv_human_approval` 为人工审批占位接口,后者**未实现**,执行器当前不消费审批响应(对应 `is_blocking` 等待逻辑亦未落地)。
+> 人工审批当前**只发不收**:`emit_human_approval_request` 发出 `WorkflowEvent`,对应的审批响应(`HumanApprovalResponse`)由 `df-nodes::human_node` 通过 `EventBus::subscribe()` 订阅后自行消费,`EventBus` 本身不再持审批响应存储/检索接口。
### 状态机(state.rs)
@@ -149,15 +148,16 @@ pub trait Node: Send + Sync {
| 方法 | 签名 | 职责 |
|------|------|------|
| `new` / `get` | `... -> Self` / `(&NodeId) -> NodeStatus` | 创建;取状态(缺失默认 `Pending`) |
-| `set_running` | `(&mut self, NodeId) -> Result<()>` | `Pending → Running` |
-| `set_completed` | `(&mut self, NodeId) -> Result<()>` | `Running → Completed` |
-| `set_failed` | `(&mut self, NodeId) -> Result<()>` | `Running → Failed` |
-| `set_waiting` | `(&mut self, NodeId)` | 设为 `Waiting`,**绕过转换校验**(直接 set) |
-| `set_skipped` | `(&mut self, NodeId)` | 设为 `Skipped`,**绕过转换校验** |
-| `is_cancelled` | `(&NodeId) -> bool` | 是否为 `Cancelled`(同样无对应 setter,外部直接 set) |
-| `snapshot` | `() -> &HashMap` | 全量状态快照引用 |
+| `set_running` | `(&self, NodeId) -> Result<()>` | `Pending → Running` |
+| `set_completed` | `(&self, NodeId) -> Result<()>` | `Running → Completed` |
+| `set_failed` | `(&self, NodeId) -> Result<()>` | `Running → Failed` |
+| `set_cancelled` | `(&self, NodeId)` | 置 `Cancelled`,**唯一受控旁路**:不经 `transition` 合法性校验直接 `insert`(人工审批取消由 `cancel_workflow_node` IPC 异步触发,节点可能处于 Running 之外的任意态,走 `is_legal` 会被拒绝) |
+| `is_cancelled` | `(&NodeId) -> bool` | 是否为 `Cancelled` |
+| `snapshot` | `() -> HashMap` | 全量状态快照(clone 返回,调用方持独立副本) |
-> `Waiting` / `Skipped` / `Cancelled` 三态暂未纳入 `is_legal` 校验链,对应的 `set_*` 直接 `insert`,可从任意态跳转。
+> **`set_waiting` / `set_skipped` 已删除**:两者同为旁路置位但全仓零调用,已删。`Waiting` / `Skipped` 两态当前无对应 setter。`Cancelled` 由 `set_cancelled` 作为唯一受控旁路置位,可从任意态跳转。
+>
+> 共享语义:内部 `Arc>`,`clone()` 为浅拷贝(Arc 引用计数 +1),所有 clone 共享同一底层 HashMap。`DagExecutor` 的 `state_machine` 与下沉到 `NodeContext.node_status` 的 clone 共享底层;`run_workflow` 把执行器状态机注册到 `AppState` 全局表后,`cancel_workflow_node` IPC 的 `set_cancelled` 可直达运行中阻塞节点(如 `HumanNode`)的 `is_cancelled` 轮询。
### Dag 对外 API(dag.rs)
@@ -177,7 +177,7 @@ pub trait Node: Send + Sync {
`ConditionEngine::evaluate(expr: &str, context: &Value) -> Result` — 仅支持 `"true"` / `"false"` 字面量(区分大小写,先 `trim()` 去空白)。
-**默认放行(安全风险)**:空串、`"True"`/`"FALSE"` 等大小写不匹配字面量、以及任意非 `true`/`false` 字面量(如 `"yes"`、`"1"`、`"$.status == 'completed'"`)均回退为 `Ok(true)` 并 `tracing::warn!`。即**条件不匹配时放行而非阻断**,DAG 边全通 —— 无法据上游输出做条件分支。
+**默认保守拒绝**:空串、`"True"`/`"FALSE"` 等大小写不匹配字面量、以及任意非 `true`/`false` 字面量(如 `"yes"`、`"1"`、`"$.status == 'completed'"`)均回退为 `Ok(false)` 并 `tracing::warn!`。即**条件未识别时阻断而非放行**(B-260614-02 反转)——条件分支写错或引擎未实现时不静默放行。
> 现状:JSON Path、比较运算、`contains`、逻辑组合均未实现(`context` 参数当前未被使用,仅占位对齐签名)。详见 [B 路线决策接入需求](#🔮-决策能力接入需求b-路线)。
@@ -211,7 +211,7 @@ crates/df-workflow/src/
AI Chat(B 路线)从单链 ReAct 升级为规划式协作时,本引擎需补两处:
-1. **`condition.rs::ConditionEngine::evaluate()`** —— 当前只认 `"true"` / `"false"` 字面量,默认 `Ok(true)`,DAG 边全通,无法据 AI 输出做条件分支。需补:
+1. **`condition.rs::ConditionEngine::evaluate()`** —— 当前只认 `"true"` / `"false"` 字面量,未识别表达式默认 `Ok(false)`(保守拒绝),无法据 AI 输出做条件分支。需补:
- JSON Path 取值(从上游节点输出读字段)
- 比较运算(`==` `!=` `>` `<` `>=` `<=`)
- `contains` / 字符串匹配