修复: df-nodes测试回归(status收口fixture改advance_status_atomic)

This commit is contained in:
2026-06-16 21:30:56 +08:00
parent b94e74a96d
commit 4bdaac5983
3 changed files with 20 additions and 3 deletions

View File

@@ -328,8 +328,11 @@ mod tests {
// 而是验证读后改路径在 from=当前库内 status 时正常推进。 // 而是验证读后改路径在 from=当前库内 status 时正常推进。
let repo = setup().await; let repo = setup().await;
repo.insert(rec("t1", "todo")).await.unwrap(); repo.insert(rec("t1", "todo")).await.unwrap();
// 旁路把 status 改成 in_progress(模拟另一路并发推进) // 另一路推进把 status 改成 in_progress(模拟并发推进,走 CAS 合法路径;
repo.update_field("t1", "status", "in_progress").await.unwrap(); // F-03 收口后 status 不在 update_field 白名单,模拟并发改态须走 advance_status_atomic)
repo.advance_status_atomic("t1", "todo", "in_progress", false)
.await
.unwrap();
// 读出来是 in_progress,推进到 in_review 合法 → 正常成功 // 读出来是 in_progress,推进到 in_review 合法 → 正常成功
let r = advance_task_atomic(&repo, "t1", "in_review").await.unwrap(); let r = advance_task_atomic(&repo, "t1", "in_review").await.unwrap();
assert_eq!(r.status, "in_review"); assert_eq!(r.status, "in_review");

View File

@@ -48,6 +48,7 @@
- [x] B-260614-01 — ~~待审批持久化根治(重启恢复)未生效~~ ✅ mission:T-260614-01 已修复commands.rs:444 clear→retain 保其他对话 pendingai_approve 两处 if !recovered 守卫移除cargo check 0 err / 19 test pass(06-14) - [x] B-260614-01 — ~~待审批持久化根治(重启恢复)未生效~~ ✅ mission:T-260614-01 已修复commands.rs:444 clear→retain 保其他对话 pendingai_approve 两处 if !recovered 守卫移除cargo check 0 err / 19 test pass(06-14)
- [x] B-260614-02 — ~~df-workflow ConditionEngine 默认 true~~ ✅ mission:T-260614-02 已修复conditions.rs:31 `Ok(true)``Ok(false)` 保守拒绝5 个原断言错误行为的测试同步改断言df-workflow 7 test pass(06-14) - [x] B-260614-02 — ~~df-workflow ConditionEngine 默认 true~~ ✅ mission:T-260614-02 已修复conditions.rs:31 `Ok(true)``Ok(false)` 保守拒绝5 个原断言错误行为的测试同步改断言df-workflow 7 test pass(06-14)
- [x] B-260614-04 — ~~df-workflow NodeRegistry::default() script 工厂 unimplemented!~~ ✅ mission:T-260614-03 已修复(删除整个 Default impl——零调用方 + 违反铁律state.rs build_registry 已用 new() + 手动注册真实 ScriptNode(06-14) - [x] B-260614-04 — ~~df-workflow NodeRegistry::default() script 工厂 unimplemented!~~ ✅ mission:T-260614-03 已修复(删除整个 Default impl——零调用方 + 违反铁律state.rs build_registry 已用 new() + 手动注册真实 ScriptNode(06-14)
- [x] B-260616-20 [P0] — **df-nodes 测试回归:`advance_succeeds_after_prior_status_change` 红(CR-260616-41 status 收口副作用)****已修**(主代 task_advance_node.rs:331-332 改 `advance_status_atomic("t1","todo","in_progress",false)` 走 CAS 合法路径模拟并发推进,亲跑 `cargo test -p df-nodes` **67 passed/0 failed/1 ignored**)。原委:commit b94e74a 把 status 移出 tasks 白名单(D-260616-04 收口),`crates/df-nodes/src/task_advance_node.rs:332` 测试用 `repo.update_field("t1","status","in_progress")` 模拟「旁路并发改 status」前置,现被白名单拒 → unwrap panic。生产代码无回归,仅测试 fixture 同步。— crates/df-nodes/src/task_advance_node.rs:331(审查 CR-260616-41 复现)
### 🔧 2026-06-16 排查会话新报 bug4 项·仅分析未实施) ### 🔧 2026-06-16 排查会话新报 bug4 项·仅分析未实施)

View File

@@ -125,13 +125,26 @@
- **待修项回流 todo**: **无**(4 low 全非阻断:clamp 冗余/extract_json 增强/semantic injection/硬编码均可在后续迭代处理,当前 PASS)。 - **待修项回流 todo**: **无**(4 low 全非阻断:clamp 冗余/extract_json 增强/semantic injection/硬编码均可在后续迭代处理,当前 PASS)。
- **主代独立核查**: ✅ 全过(adversarial.rs:143 evaluate_with_llm 真实实现+:161 complete+:380 build_adversarial_prompt+:500 serde_json 解析+:571/586 枚举 bail+:167-170 clamp / MockProvider:733-755+4 单测 a8-a11 / cargo test -p df-ideas 19 passed EXIT 0)。 - **主代独立核查**: ✅ 全过(adversarial.rs:143 evaluate_with_llm 真实实现+:161 complete+:380 build_adversarial_prompt+:500 serde_json 解析+:571/586 枚举 bail+:167-170 clamp / MockProvider:733-755+4 单测 a8-a11 / cargo test -p df-ideas 19 passed EXIT 0)。
### CR-260616-41 batch64 F-03 收口三件(advance_task/run_workflow 工具注册+update_task 拒 status+crud 白名单移 status) — 🟡 待审 ### CR-260616-41 batch64 F-03 收口三件(advance_task/run_workflow 工具注册+update_task 拒 status+crud 白名单移 status) — ✅ 已审(PASS·🔴-1已修)
- **范围**: F-260616-07 阶段3 确定性收口(workflow wii1u1lnm,3 agent 并行)。①**Agent A tool_registry.rs**:advance_task 注册(:395,handler L407 调 `df_nodes::task_advance_node::advance_task_atomic(&TaskRepo::new(&db),id,target_status)` 与 IPC `commands::task::advance_task:165` 同源,thin 转发无 AppHandle/State 依赖,RiskLevel::Medium)+ run_workflow 注册(:428,handler 架构约束无 AppHandle/State 报错引导前端转调 `invoke('run_workflow')` IPC,RiskLevel::High,ToolDefinition+审批文案 L1260-1261/1286-1287 注册)+ update_task handler guard L374 `if field=="status"` bail(schema 通用 field/value 模式非 enum,故靠 handler guard 拦非 schema 改)。②**Agent B crud.rs**:tasks allowed_columns L331-347 移 `"status"`+`"review_rounds"`(D-260616-04 收口),注释明确「status/review_rounds 走 advance_status_atomic CAS 独立路径,后人勿补回白名单」;`advance_status_atomic` L848 CAS SQL `WHERE id AND status=expected AND deleted_at IS NULL` 完全绕 `validate_column_name`/`allowed_columns_for`;`tests/project_soft_delete.rs:250` 断言反转 `update_field_rejects_tasks_status`(改 status 现 Err)。③**Agent C CR-40 审查**(对 batch62 dfe0096 evaluate_with_llm):PASS 4 low 非阻断。 - **范围**: F-260616-07 阶段3 确定性收口(workflow wii1u1lnm,3 agent 并行)。①**Agent A tool_registry.rs**:advance_task 注册(:395,handler L407 调 `df_nodes::task_advance_node::advance_task_atomic(&TaskRepo::new(&db),id,target_status)` 与 IPC `commands::task::advance_task:165` 同源,thin 转发无 AppHandle/State 依赖,RiskLevel::Medium)+ run_workflow 注册(:428,handler 架构约束无 AppHandle/State 报错引导前端转调 `invoke('run_workflow')` IPC,RiskLevel::High,ToolDefinition+审批文案 L1260-1261/1286-1287 注册)+ update_task handler guard L374 `if field=="status"` bail(schema 通用 field/value 模式非 enum,故靠 handler guard 拦非 schema 改)。②**Agent B crud.rs**:tasks allowed_columns L331-347 移 `"status"`+`"review_rounds"`(D-260616-04 收口),注释明确「status/review_rounds 走 advance_status_atomic CAS 独立路径,后人勿补回白名单」;`advance_status_atomic` L848 CAS SQL `WHERE id AND status=expected AND deleted_at IS NULL` 完全绕 `validate_column_name`/`allowed_columns_for`;`tests/project_soft_delete.rs:250` 断言反转 `update_field_rejects_tasks_status`(改 status 现 Err)。③**Agent C CR-40 审查**(对 batch62 dfe0096 evaluate_with_llm):PASS 4 low 非阻断。
- **维度**: ①advance_task handler 同源核验(tool_registry.rs:407 调 advance_task_atomic 与 commands::task::advance_task:165 同源,thin 转发无逻辑分叉) ②update_task handler guard 双重防御(L374 handler 拒 + df-storage 白名单拒,status 双层拦截) ③crud 白名单移 status+review_rounds 收口语义(advance_status_atomic L848 CAS 独立路径不经 allowed_columns,review_rounds 唯一写入路径防计数错乱) ④run_workflow handler 架构约束(handler 无 AppHandle/State 报错引导走 IPC 是否合理,后续批注入 AppState 句柄让 AI 直驱) ⑤审批文案映射(display_hint_for_tool L1260-1261 + tool_display_hint L1286-1287 advance_task/run_workflow 条目) ⑥project_soft_delete.rs 测试断言反转(update_field_rejects_tasks_status 改 status 现 Err 正确) ⑦前端 status 路径零残留(taskApi.update 全代码库零调用方,status 唯一走 taskApi.advance→advance_task IPC) ⑧commands/task.rs update_task IPC 残留死代码(task.rs:100-108 field=="status" 分支移白名单后永不命中,回流 todo 清理)。 - **维度**: ①advance_task handler 同源核验(tool_registry.rs:407 调 advance_task_atomic 与 commands::task::advance_task:165 同源,thin 转发无逻辑分叉) ②update_task handler guard 双重防御(L374 handler 拒 + df-storage 白名单拒,status 双层拦截) ③crud 白名单移 status+review_rounds 收口语义(advance_status_atomic L848 CAS 独立路径不经 allowed_columns,review_rounds 唯一写入路径防计数错乱) ④run_workflow handler 架构约束(handler 无 AppHandle/State 报错引导走 IPC 是否合理,后续批注入 AppState 句柄让 AI 直驱) ⑤审批文案映射(display_hint_for_tool L1260-1261 + tool_display_hint L1286-1287 advance_task/run_workflow 条目) ⑥project_soft_delete.rs 测试断言反转(update_field_rejects_tasks_status 改 status 现 Err 正确) ⑦前端 status 路径零残留(taskApi.update 全代码库零调用方,status 唯一走 taskApi.advance→advance_task IPC) ⑧commands/task.rs update_task IPC 残留死代码(task.rs:100-108 field=="status" 分支移白名单后永不命中,回流 todo 清理)。
- **commit**: 待提交(batch64 攒批)。 - **commit**: 待提交(batch64 攒批)。
- **主代独立核查**: ✅ 全过(tool_registry.rs advance_task:395+handler:407 advance_task_atomic 同源 / run_workflow:428+handler 约束报错引导 IPC / update_task handler guard:374 `field=="status"` bail / crud.rs:331-347 tasks 白名单移 status+review_rounds 注释明确 / advance_status_atomic:848 CAS `WHERE id AND status=expected AND deleted_at IS NULL` 独立路径 / cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试含新 update_field_rejects_tasks_status)。 - **主代独立核查**: ✅ 全过(tool_registry.rs advance_task:395+handler:407 advance_task_atomic 同源 / run_workflow:428+handler 约束报错引导 IPC / update_task handler guard:374 `field=="status"` bail / crud.rs:331-347 tasks 白名单移 status+review_rounds 注释明确 / advance_status_atomic:848 CAS `WHERE id AND status=expected AND deleted_at IS NULL` 独立路径 / cargo check --workspace EXIT 0(5 pre-existing warnings 无关)+ df-storage 11 集成测试含新 update_field_rejects_tasks_status)。
- **审查 agent 待复审重点**: ①advance_task handler 同源 IPC(commands::task::advance_task:165 核验,thin 转发无逻辑分叉,df_core::error::Error→anyhow ?From 链路) ②update_task handler guard 双重防御(handler guard L374 + df-storage 白名单双拦,是否漏路径) ③crud 白名单移 status+review_rounds 收口(advance_status_atomic L848 CAS 是否真独立不经 allowed_columns,review_rounds 唯一写入路径防旁路写) ④run_workflow handler 报错引导 IPC 是否合理(架构约束无 AppHandle/State,后续批注入句柄) ⑤审批文案映射完整(display_hint L1260-1261/1286-1287) ⑥测试断言反转正确(project_soft_delete.rs:250 update_field_rejects_tasks_status 改 status 现 Err) ⑦commands/task.rs:100-108 update_task IPC 残留死代码回流 todo。 - **审查 agent 待复审重点**: ①advance_task handler 同源 IPC(commands::task::advance_task:165 核验,thin 转发无逻辑分叉,df_core::error::Error→anyhow ?From 链路) ②update_task handler guard 双重防御(handler guard L374 + df-storage 白名单双拦,是否漏路径) ③crud 白名单移 status+review_rounds 收口(advance_status_atomic L848 CAS 是否真独立不经 allowed_columns,review_rounds 唯一写入路径防旁路写) ④run_workflow handler 报错引导 IPC 是否合理(架构约束无 AppHandle/State,后续批注入句柄) ⑤审批文案映射完整(display_hint L1260-1261/1286-1287) ⑥测试断言反转正确(project_soft_delete.rs:250 update_field_rejects_tasks_status 改 status 现 Err) ⑦commands/task.rs:100-108 update_task IPC 残留死代码回流 todo。
- **复审结论(2026-06-16·审查 agent 亲跑 cargo + 独立 grep/read 核验源码)**: ⚠ **ISSUES** — 🔴1 🟡0 ⚪1 → **修复后(主代 b94e74a 之后)**: ✅ **PASS** 🔴0 🟡0 ⚪1(🔴-1 测试 fixture 已修)
- **验证**: `cargo check --workspace` **EXIT 0**(5 pre-existing dead_code warnings 无关本 CR)/ `cargo test -p df-storage` **33 passed**(22 lib + 11 project_soft_delete 含新 `update_field_rejects_tasks_status` ✅)/ `cargo test -p df-nodes` **66 passed; 1 FAILED**(`advance_succeeds_after_prior_status_change` 红,见 🔴-1)→ **🔴-1 修复后 67 passed; 0 failed; 1 ignored**(task_advance_node.rs:331-332 改 advance_status_atomic)。
- **①advance_task handler 同源核验** ✅ tool_registry.rs:407 调 `advance_task_atomic(&TaskRepo::new(&db), id, target_status)` 与 commands::task::advance_task(task.rs:165)同源调 `df_nodes::task_advance_node::advance_task_atomic(&state.tasks, &id, &target_status)`,均 thin 转发到同一 `advance_task_atomic`(task_advance_node.rs:43-90),无逻辑分叉。df_core::error::Error 是 thiserror(crate df-core/src/error.rs:5 `#[derive(Error)]`),实现 std::error::Error,anyhow::Error: From<E> 链路通(`?` 编译过 cargo check 证)。
- **②update_task handler guard 双重防御** ✅ tool_registry.rs:374-378 `if field=="status"` bail(第一层 handler 拒)+ tool_registry.rs:380-382 `is_allowed_column("tasks", field)` 查 crud.rs:331-347 白名单(第二层,白名单已无 status),status 双层拦截。无漏路径:value 必经 args["field"].as_str() 解析后两条 guard 之一必中。
- **③crud 白名单移 status+review_rounds 收口** ✅ crud.rs:342 现 tasks 白名单 `"project_id","title","description","priority","branch_name","assignee","workflow_def_id","base_branch","updated_at"`(无 status/review_rounds)。advance_status_atomic(crud.rs:848-880)走独立 CAS SQL `UPDATE tasks SET status=?1[, review_rounds=review_rounds+1] WHERE id=?3 AND status=?4 AND deleted_at IS NULL`,**不经 validate_column_name/allowed_columns_for**(无白名单调用)。review_rounds 唯一写入路径收口语义达成:除 advance_status_atomic 退回转换 bump_rounds=true 路径,无任何旁路可写(crud.rs:865-866 SQL 内联 `review_rounds+1`)。
- **④run_workflow handler 报错引导 IPC** ✅ 合理。tool_registry.rs:431-446 handler 仅持 `_db: Arc<Database>`(L432 实际未用,前缀 `_` 标记),无法构造 AppHandle/AppState(registry/event_bus/workflows Repo),L440-444 显式 anyhow 返回明确错误引导前端 `invoke('run_workflow', {task_id, target_status})`。符合单一执行路径原则(不在 handler 重放 DAG 引擎)。注释 L419-426 标注后续批注入 AppState 句柄扩展路径。
- **⑤审批文案映射** ✅ display_hint_for_tool(tool_registry.rs:1259-1261)注册 `advance_task => "推进任务状态:{} → {}", &["id","target_status"]` + `run_workflow => "触发工作流:任务{} 推进到 {}", &["task_id","target_status"]`;tool_display_hint(tool_registry.rs:1286-1287)注册 `advance_task => Some("推进任务状态")` + `run_workflow => Some("触发工作流")`。两条目齐全。
- **⑥project_soft_delete.rs 测试断言反转** ✅ project_soft_delete.rs:250 `update_field_rejects_tasks_status``res.is_err()`(L259-262)+ 对照 `assert_ne!(rec.status, "done")`(L266)。亲跑 11 集成测试全过 EXIT 0。
- **⑦前端 status 路径零残留** ✅ taskApi.update(src/api/task.ts:17-19)仅 tasks.ts:31 `updateTask` 调用,全代码库 grep `updateTask(` 零组件调用方(仅定义无消费)。TaskDetail.vue:281 推进走 `taskApi.advance(task.value.id, target)`(状态唯一路径→advance_task IPC)。status 前端路径零残留达成。注:tasks.ts:29 `updateTask` 函数本身保留(改 title/description 等非 status 字段,合法),仅 status 字段已无消费方。
- **⑧commands/task.rs update_task IPC 残留分支** ⚪(low)task.rs:102-108 `if field=="status" && !TaskStatus::is_valid(&value)` 分支并非纯死代码:它仍为 status **非法值**(拼写错/越界)提供更友好早错误(先于白名单拒)。但 status **合法值**路径必落到 task.rs:133-135 `update_field` 被白名单拒,故「校验通过后写入」分支永不命中。建议保留 value 校验(改善 UX)+ 删除成功路径假设注释,或整体移除改由 advance_task 独占(后续迭代决定,非阻断)。
- **🔴-1(df-nodes 测试回归,high)**: **task_advance_node.rs:332** `repo.update_field("t1", "status", "in_progress").await.unwrap()` 测试红 —— 退回测试 `advance_succeeds_after_prior_status_change`(L324-336)用 `update_field` 模拟「旁路并发改 status」前置,但本 commit 已把 status 移出 tasks 白名单,`update_field` 现 Err → `unwrap()` panic「表 tasks 不允许的字段名: status」。亲跑 `cargo test -p df-nodes` 证实 66 passed/1 failed。**生产代码无回归**(advance_task_atomic 本身工作正常,66 个推进链测试全过),仅测试 fixture 需同步:把 L332 改成 `repo.advance_status_atomic("t1","todo","in_progress",false).await.unwrap()`(用 CAS 路径模拟并发改,语义等价)或直接 `repo.insert(rec("t1","in_progress"))` 重建前置态。CI 红门未守,须修后才能合。 **✅ 已修(主代 task_advance_node.rs:331-332 改 `advance_status_atomic("t1","todo","in_progress",false)` 走 CAS 合法路径模拟并发推进,亲跑 cargo test -p df-nodes 67 passed/0 failed/1 ignored)**
- **⚪ low 清单**: ①task.rs:102-108 status value 校验分支半死代码(合法值成功路径永不命中,非法值仍触发,见 ⑧)。
- **待修项回流 todo**: **🔴-1**(df-nodes 测试回归 task_advance_node.rs:332) **✅ 已修**(主代改 `advance_status_atomic`,亲跑 67 passed,见上方修复记录)。低 ①task.rs:102-108 status value 校验半死代码非阻断(审查建议保留 value 校验改善 UX + 删成功路径假设注释,后续迭代决定)。
--- ---