修复: status→enum + type alias(SMELL-P1-6)

This commit is contained in:
2026-06-28 04:42:59 +08:00
parent 48c966f6f7
commit d1b9488853
21 changed files with 304 additions and 126 deletions

View File

@@ -142,6 +142,7 @@ mod idea_source_test_helpers {
use df_storage::crud::IdeaRepo;
use df_storage::db::Database;
use df_storage::models::IdeaRecord;
use df_types::types::IdeaStatus;
/// 建内存 DB(已跑 migrations,含 ideas 表)+ 插一条 IdeaRecord fixture,返回 db 句柄。
///
@@ -155,7 +156,7 @@ mod idea_source_test_helpers {
id: id.to_string(),
title: format!("fixture-{}", id),
description: String::new(),
status: "draft".to_string(),
status: IdeaStatus::Draft,
priority: 1,
score: None,
tags: None,

View File

@@ -88,7 +88,7 @@ impl MentionResolver for ProjectResolver {
ref_id: id.clone(),
})?;
// status: String → ProjectStatus(未知字符串按 Planning 兜底,不阻断注入)
let status = ProjectStatus::from_db_str(&record.status).unwrap_or(ProjectStatus::Planning);
let status = ProjectStatus::from_db_str(record.status.as_str()).unwrap_or(ProjectStatus::Planning);
// path 脱敏:None(未绑定)保持 None;Some 则按 locality 脱敏包 newtype
let path = record
.path
@@ -113,7 +113,7 @@ impl MentionResolver for ProjectResolver {
}).await {
if !tasks.is_empty() {
let mut task_lines: Vec<String> = tasks.iter().map(|t| {
format!(" - {} ({})", t.title, t.status)
format!(" - {} ({})", t.title, t.status.as_str())
}).collect();
task_lines.insert(0, format!("进行中任务({}):", tasks.len()));
lines.push(task_lines.join("\n"));
@@ -126,9 +126,9 @@ impl MentionResolver for ProjectResolver {
..Default::default()
}).await {
let related: Vec<_> = ideas.iter()
.filter(|i| i.status == "pending_review")
.filter(|i| i.status.as_str() == "pending_review")
.take(3)
.map(|i| format!(" - {} ({})", i.title, i.status))
.map(|i| format!(" - {} ({})", i.title, i.status.as_str()))
.collect();
if !related.is_empty() {
let mut idea_lines = vec![format!("待评估灵感({}):", related.len())];
@@ -189,7 +189,7 @@ impl MentionResolver for TaskResolver {
kind: "task".to_string(),
ref_id: id.clone(),
})?;
let status = TaskStatus::from_db_str(&task.status).unwrap_or(TaskStatus::Todo);
let status = TaskStatus::from_db_str(task.status.as_str()).unwrap_or(TaskStatus::Todo);
// join project_name:project_id 取 ProjectRecord.name;失败/无对应 None(非错误)
let project_name = {
let project_repo = ProjectRepo::new(&self.db);
@@ -253,7 +253,7 @@ impl MentionResolver for IdeaResolver {
kind: "idea".to_string(),
ref_id: id.clone(),
})?;
let status = IdeaStatus::from_db_str(&record.status).unwrap_or(IdeaStatus::Draft);
let status = IdeaStatus::from_db_str(record.status.as_str()).unwrap_or(IdeaStatus::Draft);
Ok(Augmentation::Idea {
id: record.id,
title: record.title,

View File

@@ -162,7 +162,7 @@ pub(crate) async fn build_system_prompt_with_excluded(
if excluded_project_ids.iter().any(|id| id == &p.id) {
continue;
}
prompt.push_str(&format!("- {} ({}): {}\n", p.name, p.status, p.description));
prompt.push_str(&format!("- {} ({}): {}\n", p.name, p.status.as_str(), p.description));
if let Some(ref dir) = p.path {
prompt.push_str(&format!(" 目录: {}\n", dir));
}
@@ -181,7 +181,7 @@ pub(crate) async fn build_system_prompt_with_excluded(
if excluded_task_ids.iter().any(|id| id == &tk.id) {
continue;
}
prompt.push_str(&format!("- {} ({}): {}\n", tk.title, tk.status, tk.description));
prompt.push_str(&format!("- {} ({}): {}\n", tk.title, tk.status.as_str(), tk.description));
}
// 机制层注明语(中/英):仅最近 20 条,全量/按项目查询走 list_tasks
prompt.push_str(&tasks_listed_note(lang));

View File

@@ -11,7 +11,7 @@ use df_execute::shell::{execute, ShellRequest};
use df_storage::db::Database;
use df_storage::models::{ProjectRecord, ProjectServiceRecord, TaskRecord, IdeaRecord};
use df_types::types::new_id;
use df_types::types::{new_id, IdeaStatus, ProjectStatus, TaskStatus};
use crate::commands::now_millis;
use crate::state::AllowedDirs;
@@ -573,7 +573,7 @@ fn register_project_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
let repo = df_storage::crud::ProjectRepo::new(&db);
let record = ProjectRecord {
id: new_id(), name: name.to_string(), description: description.to_string(),
status: "planning".to_string(), idea_id: None,
status: ProjectStatus::Planning, idea_id: None,
path: None, stack: None,
created_at: now_millis(), updated_at: now_millis(),
};
@@ -683,7 +683,7 @@ fn register_task_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
};
// 按状态过滤(可选):todo/in_progress/in_review/testing/blocked/done/cancelled
if let Some(status) = args.get("status").and_then(|v| v.as_str()) {
tasks.retain(|t| t.status == status);
tasks.retain(|t| t.status.as_str() == status);
}
let total = tasks.len();
let offset = args["offset"].as_u64().unwrap_or(0) as usize;
@@ -767,7 +767,7 @@ fn register_task_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
id: new_id(), project_id: project_id.to_string(), title: title.to_string(),
description: args["description"].as_str().unwrap_or("").to_string(),
// priority 默认 2(medium):与 commands::task::default_priority 一致,新任务默认中优先级(非 high)
status: "todo".to_string(), priority: args["priority"].as_i64().unwrap_or(2) as i32,
status: TaskStatus::Todo, priority: args["priority"].as_i64().unwrap_or(2) as i32,
branch_name: None, assignee: None, workflow_def_id: None, base_branch: None,
review_rounds: 0,
output_json: None,
@@ -1032,13 +1032,13 @@ fn register_task_graph_tools(registry: &mut AiToolRegistry, db: &Arc<Database>)
"backlog" => "todo".to_string(),
"active" => {
if ACTIVE_OK_STATUSES.contains(&current.status.as_str()) {
current.status.clone()
current.status.as_str().to_string()
} else {
"in_progress".to_string()
}
}
"todo" => "todo".to_string(),
"decision" => current.status.clone(),
"decision" => current.status.as_str().to_string(),
_ => unreachable!("queue 白名单已收口"),
};
@@ -1047,7 +1047,7 @@ fn register_task_graph_tools(registry: &mut AiToolRegistry, db: &Arc<Database>)
repo.update_field(id, "queue", &new_queue).await?;
}
// 写 status(专用 set_status_for_aggregation 绕过 status 收口,合法非状态机路径)
if current.status != new_status {
if current.status.as_str() != new_status {
repo.set_status_for_aggregation(id, &new_status).await?;
}
@@ -1354,7 +1354,7 @@ fn register_idea_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
id: new_id(), title: title.to_string(),
description: args["description"].as_str().unwrap_or("").to_string(),
// priority 默认 1:灵感默认普通优先级(与 commands::idea::default_priority 及 tasks 表 SQL DEFAULT 1 对齐)
status: "draft".to_string(), priority: args["priority"].as_i64().unwrap_or(1) as i32,
status: IdeaStatus::Draft, priority: args["priority"].as_i64().unwrap_or(1) as i32,
score: None, tags: args["tags"].as_str().map(|s| s.to_string()),
source: args["source"].as_str().map(|s| s.to_string()),
promoted_to: None, ai_analysis: None, scores: None,

View File

@@ -6,7 +6,7 @@ use serde::Deserialize;
use tauri::State;
use df_ai::provider::LlmProvider;
use df_types::types::{new_id, Priority};
use df_types::types::{new_id, IdeaStatus, Priority, ProjectStatus};
use df_ideas::capture::Idea;
use df_storage::crud::{is_unique_constraint_err, IdeaQuery};
use df_storage::models::{IdeaEvaluationRecord, IdeaRecord, ProjectEventRecord, ProjectRecord};
@@ -87,7 +87,7 @@ pub async fn create_idea(
id: new_id(),
title: input.title,
description: input.description,
status: "draft".to_string(),
status: IdeaStatus::Draft,
priority: input.priority,
score: None,
tags: input.tags,
@@ -190,7 +190,7 @@ pub async fn promote_idea(
id: project_id.clone(),
name: project.name,
description: project.description,
status: "planning".to_string(),
status: ProjectStatus::Planning,
idea_id: Some(id.clone()),
path: None,
stack: None,
@@ -208,7 +208,7 @@ pub async fn promote_idea(
// 灵感状态未变的数据不一致)。Repository 方法各自持锁不支持跨 repo 共享事务对象,故选补偿
// 删除而非真事务(改动最小,工程投入产出比最高)。
let updated = IdeaRecord {
status: "promoted".to_string(),
status: IdeaStatus::Promoted,
promoted_to: Some(project_id.clone()),
updated_at: now,
..record
@@ -338,7 +338,7 @@ pub async fn evaluate_idea(
scores: Some(scores_json.clone()),
ai_analysis: Some(ai_analysis.clone()),
score: Some(score_value as f64),
status: "pending_review".to_string(),
status: IdeaStatus::PendingReview,
updated_at: now_millis(),
..record
};
@@ -460,7 +460,7 @@ fn record_to_idea(record: &IdeaRecord) -> Idea {
title: record.title.clone(),
description: record.description.clone(),
// IDEA-FIX-05: 读真实 status(原硬编码 Draft 丢真实状态,评估上下文完整性)
status: status_from_str(&record.status),
status: status_from_str(record.status.as_str()),
priority: priority_from_i32(record.priority),
scores: None,
tags,

View File

@@ -12,7 +12,7 @@ use df_ai::provider::{ChatMessage, CompletionRequest};
use df_ai::router::{
select_model_id, Modality, TaskRequirements,
};
use df_types::types::new_id;
use df_types::types::{new_id, ProjectStatus};
use df_project::scan::{
collect_sample, detect_stack, discover_projects, extract_description,
normalize_path, DiscoveredProject,
@@ -156,7 +156,7 @@ async fn create_with_binding(
id: new_id(),
name,
description,
status: "planning".to_string(),
status: ProjectStatus::Planning,
idea_id,
path,
stack,

View File

@@ -284,7 +284,7 @@ pub async fn create_task(
project_id: input.project_id,
title: input.title,
description: input.description,
status: "todo".to_string(),
status: TaskStatus::Todo,
priority: input.priority,
branch_name: input.branch_name,
assignee: input.assignee,
@@ -314,7 +314,7 @@ pub async fn create_task(
Some("task"),
Some(&record.id),
None,
Some(&record.status),
Some(record.status.as_str()),
)
.await;
Ok(record)
@@ -466,8 +466,8 @@ pub async fn advance_task(
"task_advanced",
Some("task"),
Some(&updated.id),
from_state.as_deref(),
Some(&updated.status),
from_state.as_ref().map(TaskStatus::as_str),
Some(updated.status.as_str()),
)
.await;
@@ -505,7 +505,7 @@ async fn recompute_parent_status(state: &State<'_, AppState>, parent_id: &str) -
.get_by_id(parent_id)
.await
.map_err(err_str)?
.map(|t| t.status)
.map(|t| t.status.as_str().to_string())
.ok_or_else(|| format!("父任务 {parent_id} 不存在"));
}
@@ -541,7 +541,7 @@ async fn recompute_parent_status(state: &State<'_, AppState>, parent_id: &str) -
.await
.map_err(err_str)?
.ok_or_else(|| format!("父任务 {parent_id} 不存在"))?;
if current.status == new_status {
if current.status.as_str() == new_status {
return Ok(new_status);
}
state
@@ -688,13 +688,13 @@ pub async fn move_task_queue(
"backlog" => "todo".to_string(),
"active" => {
if ACTIVE_OK_STATUSES.contains(&current.status.as_str()) {
current.status.clone() // 已在执行中三态,保留
current.status.as_str().to_string() // 已在执行中三态,保留
} else {
"in_progress".to_string() // 否则强制进 in_progress(执行中池默认执行态)
}
}
"todo" => "todo".to_string(), // 待办池任务 status 强制=todo(从 active 退回 todo 池即重置执行态)
"decision" => current.status.clone(), // 待决策池保留当前 status(暂停推进不重置执行态)
"decision" => current.status.as_str().to_string(), // 待决策池保留当前 status(暂停推进不重置执行态)
_ => unreachable!("validate_queue 已收口"),
};
@@ -708,7 +708,7 @@ pub async fn move_task_queue(
.map_err(err_str)?;
}
// 写 status(专用 set_status_for_aggregation 绕过 status 收口,move_task_queue 是合法非状态机路径)
if current.status != new_status {
if current.status.as_str() != new_status {
state
.tasks
.set_status_for_aggregation(&id, &new_status)