优化: 边界加固(AI loop竞态根治+数据/审批/反馈/并发/错误分类)

AI loop 竞态(P0):per-conv epoch/owner token + 存活心跳治 force_send 双loop + stop 3s兜底误判;旧loop stale 全跳过(guard/emit/save)

agentic 收尾(A2-B8):Fatal 退出落库user消息(镜像Exhausted)+ 入口早退补save + usage is_estimated 打标 + emit_ai_completed_once 单点收敛清审批残留

聊天清理(A2-B9):clearChat 先停loop→DB单事务→内存清(clear_conversation_atomic)+ 前端错误气泡

循环并发(A2-B11):三态 ProviderAcquire(NotConfigured/Acquired/Exhausted)+ 候选循环非阻塞+防抖3次饱和降级+单测

错误分类(A2-B12):stream error帧接入 classify_status_or_class + 关键词保守降级 + 7单测

数据(G1.2/G1.4):purge_with_descendants 级联补全(11表单事务+存在性守卫)+ move_task_queue 单事务收口(两调用方共用)

git只读(G3.1):run_git_status/diff/log success判定(exit_code差异语义,失败结构化{success:false,error})

安全(G5.2/G5.6):create_project 目录Err+name校验 + module.rs 路径遍历DRY(分段匹配修a..b.rs误伤)

幂等(V2/V32):裸ALTER全守卫化 + v1..v40全链重跑幂等测试(16过)

附:remote_bridge await 临时引用修(E0716)+ agentic emit 收敛 E0716 app_state 绑定修
This commit is contained in:
lxy
2026-08-05 22:10:32 +08:00
parent ec9f0bf1ea
commit 5667da6cf4
16 changed files with 1696 additions and 215 deletions
+12 -2
View File
@@ -152,6 +152,12 @@ async fn create_with_binding(
path: Option<String>,
stack: Option<String>,
) -> Result<ProjectRecord, String> {
// G5.2: name trim + 拒空前置到 insert 之前(防空名/纯空白入库)。
// create_project / import_project / import_projects_batch 均走本函数。
let name = name.trim().to_string();
if name.is_empty() {
return Err("项目名不能为空".to_string());
}
// 绑定目录:校验存在 + 防重复 + 自动探测技术栈
let (path, stack) = match path.as_deref().map(str::trim).filter(|p| !p.is_empty()) {
Some(p) => {
@@ -159,9 +165,11 @@ async fn create_with_binding(
if p.contains("..") {
return Err(format!("路径不得包含 '..': {}", p));
}
// G5.2: 目录不存在返 Err(不再静默 create_dir_all 假成功绑空目录)。
// 对齐 import_project 语义——手误路径应提示确认而非静默建空目录。
// 确需自动建目录时,由调用方先建目录再创建/绑定。
if !Path::new(p).is_dir() {
// 目录不存在则自动创建(消除建项目→建目录死锁)
std::fs::create_dir_all(p).map_err(|e| format!("目录创建失败: {}", e))?;
return Err(format!("目录不存在: {}", p));
}
let normalized = normalize_path(p);
if let Some(conflict) = find_binding_conflict(state, &normalized, None).await? {
@@ -213,6 +221,8 @@ async fn create_with_binding(
sort_order: 0,
created_at: now_str.clone(),
updated_at: now_str,
description: None,
status: Some("active".to_string()),
};
if let Err(e) = state.project_modules.insert(module).await {
tracing::warn!("创建项目时自动建工程失败(非阻断): {}", e);