修复: gen_stream判重+重试分类+状态机最短路径+既有测试修复

This commit is contained in:
lxy
2026-08-12 00:29:06 +08:00
parent 9407f821b9
commit d31a27512c
36 changed files with 2614 additions and 264 deletions
+19 -33
View File
@@ -14,7 +14,7 @@ use crate::state::AppState;
use super::{err_str, now_millis};
// ============================================================
// 知识图谱 Phase 2:事件流埋点辅助(best-effort,对标设计 §2.4 hook/after + §10.1)
// 事件流埋点辅助(best-effort)
// ============================================================
/// 追加一条项目事件到 project_events(best-effort)。
@@ -75,17 +75,17 @@ pub struct CreateTaskInput {
/// 空字符串视为 None(向后兼容)。
#[serde(default)]
pub module_id: Option<String>,
/// 管理维度池(知识图谱 Phase 1 V29,对标设计 §2.1)。默认 "todo"(待办池)。
/// 管理维度池。默认 "todo"(待办池)。
/// 合法值:backlog / todo / decision / active / done。非法值在 IPC 层兜底校验。
/// 空字符串视为默认 todo(向后兼容,与 idea_id 一致处理)。
#[serde(default = "default_queue")]
pub queue: String,
/// 父任务 ID(知识图谱 Phase 1 V29)。默认 None = 叶子任务。
/// 父任务 ID。默认 None = 叶子任务。
/// 非空 = 子任务(限制 1 级嵌套,无孙任务:父任务自身不能有 parent_id,由 IPC 层校验)。
/// 空字符串视为 None(向后兼容)。
#[serde(default)]
pub parent_id: Option<String>,
/// 结构化需求规格 JSON 字符串(知识图谱 Phase 1 V29)。
/// 结构化需求规格 JSON 字符串。
/// 结构 { background, acceptance_criteria[], scope[], technical_design, custom_fields }。
/// None = 无结构化规格(纯文本 description)。
#[serde(default)]
@@ -102,7 +102,7 @@ fn default_queue() -> String {
}
// ============================================================
// 知识图谱 Phase 1:queue 白名单 + queue/status 一致性约束(IPC 层校验,对标设计 §2.1)
// queue 白名单 + queue/status 一致性约束(IPC 层校验)
// ============================================================
/// queue 合法值白名单(对标设计 §2.1 queue 字段语义)。
@@ -235,7 +235,7 @@ pub async fn get_task_by_id(
/// 创建任务,返回完整记录
///
/// 知识图谱 Phase 1 V29(对标设计 §2.1):扩展 queue/parent_id/content_json 可选参数(向后兼容,
/// 扩展 queue/parent_id/content_json 可选参数(向后兼容,
/// 旧调用方不传等价改造前行为)。三个新参数的 IPC 层校验:
/// - `queue`:白名单校验(validate_queue)+ queue/status 一致性(create_task 时 status 恒 todo,
/// 仅 backlog/todo/decision 合法;active/done 需经 move_task_queue 或 advance_task 流转)。
@@ -325,7 +325,7 @@ pub async fn create_task(
idea_id: input.idea_id.filter(|s| !s.is_empty()),
// 多工程:可选关联到具体工程(module)
module_id: input.module_id.filter(|s| !s.is_empty()),
// 知识图谱 Phase 1 V29 三列:经上方校验的 queue / parent_id / content_json
// 经上方校验的 queue / parent_id / content_json
queue,
parent_id,
content_json,
@@ -337,7 +337,7 @@ pub async fn create_task(
.insert(record.clone())
.await
.map_err(err_str)?;
// 知识图谱 Phase 2(对标设计 §2.4 hook/after):task_created 事件。best-effort 不阻断。
// task_created 事件。best-effort 不阻断。
emit_event(
&state,
&record.project_id,
@@ -591,34 +591,21 @@ pub async fn restore_task(state: State<'_, AppState>, id: String) -> Result<bool
/// 推进任务状态(任务推进链 F-260616-02,推进链唯一 status 写入路径)。
///
/// thin 入口(D-260616-03):业务逻辑(状态机校验 + 原子 CAS + review_rounds 累加 + 父聚合)
/// 落 df-nodes::task_advance_node,本命令只做参数转发与错误串化。
/// 落 df-nodes::task_advance_node,本命令只做参数转发与结果包装。
///
/// 流程:读当前态 → can_transition 校验 → 下沉 SQL `WHERE id AND status=expected`
/// 防 TOCTOU → 退回转换一并 review_rounds+=1。失败均返回 Err(状态机/TOCTOU/任务不存在)。
///
/// **知识图谱 Phase 1 V29 父聚合(设计 §2.1)**:推进走 `advance_task_with_parent`,推进完成后
/// 若任务有 parent_id,自动触发父任务 status 重算(recompute_parent_status,df-nodes 共享层
/// D3)。父任务=容器模型,status 不走状态机,由子任务聚合计算。聚合失败不阻断推进(best-effort,
/// 设计 §十一「事件流写入失败不阻断主操作」同类宽容语义)。
///
/// 返回:推进成功后的最新 TaskRecord(含新 status / 累加后的 review_rounds)。
/// 返回: `Result<TaskRecord, String>` — 成功返更新后的任务;失败 Err(状态机拒绝/CAS 冲突/
/// 库错误)走 Tauri reject,前端 `.catch` 捕获(与 list/update/create 单轨范式一致)。
#[tauri::command]
pub async fn advance_task(
state: State<'_, AppState>,
id: String,
target_status: String,
) -> Result<TaskRecord, String> {
// 知识图谱 Phase 2:推进前读当前态,作 task_advanced 事件 from_state(仅一次轻量读,
// advance 低频无压力)。失败(任务不存在)不阻断——后续 atomic 会用 NotFound 拒绝,from 留空。
let from_state = state
.tasks
.get_by_id(&id)
.await
.map_err(err_str)?
.map(|t| t.status);
let from_state = match state.tasks.get_by_id(&id).await {
Ok(Some(t)) => Some(t.status),
_ => None,
};
// 推进 + 父聚合(df-nodes 共享层 D3,与 AI 工具/MCP 同源,消除双轨):
// 子任务推进成功后自动触发父 status 聚合(容器模型,聚合失败仅 warn 不阻断)。
let updated = df_nodes::task_advance_node::advance_task_with_parent(
&state.tasks,
&id,
@@ -627,7 +614,6 @@ pub async fn advance_task(
.await
.map_err(err_str)?;
// 知识图谱 Phase 2(对标设计 §2.4 hook/after):task_advanced 事件。best-effort 不阻断。
emit_event(
&state,
&updated.project_id,
@@ -643,7 +629,7 @@ pub async fn advance_task(
}
// ============================================================
// 知识图谱 Phase 1:task_link CRUD IPC(对标设计 §2.2 + §五 AI 工具表)
// task_link CRUD IPC
// ============================================================
/// 创建任务横向关联(对标设计 §2.2,AI 拓扑排序编排调度的基础)。
@@ -737,7 +723,7 @@ pub async fn list_task_links(
}
// ============================================================
// 知识图谱 Phase 1:move_task_queue(跨池移动 + 一致性约束,对标设计 §2.1 + §五)
// move_task_queue(跨池移动 + 一致性约束)
// ============================================================
/// 跨池移动任务(对标设计 §2.1 queue 字段语义 + §五 move_task_queue)。
@@ -785,7 +771,7 @@ pub async fn move_task_queue(
.map_err(err_str)?
.ok_or_else(|| format!("任务 {id} 不存在"))?;
// 知识图谱 Phase 2(对标设计 §2.4 hook/after):queue 变化事件。best-effort 不阻断。
// queue 变化事件。best-effort 不阻断。
// 仅在 queue 实际变化时埋点(避免 no-op 移动产噪音事件)。
//
// M17:用独立 event_type "task_queue_moved"(非复用 "task_advanced"),与 status 推进事件
@@ -826,7 +812,7 @@ pub async fn move_task_queue(
}
// ============================================================
// 知识图谱 Phase 1:get_task_tree(父子任务树,对标设计 §2.1 + §五)
// get_task_tree(父子任务树)
// ============================================================
/// 任务树节点(父 + 子任务列表,对标设计 §2.1 限 1 级嵌套)。