新增: 批次工作落地(推进链/评估闭环/事件总线/并发/加固) + 技术债清理 + 文档整理
后端: - 工作流推进链(D-03):advance_task/状态机/闸门走 df-nodes Node trait,conditions 条件引擎扩展 - 想法评估闭环:启发式评分+对抗评估,df-ideas/scoring + df-storage/idea_eval_repo + idea 前端打通 - 全局事件数据总线:df-ai/context+context_helpers+augmentation 跨模块解耦 - AI planner/plan_hint/intent:aichat B 路线并行多轮基础 - patch_file 加固(TD-03/04):读改写整体锁防 lost update,expected_hash 合约闭环 - 压缩超时兜底(F-15 卡死根治) - F-09 多会话并发:LlmConcurrency per-conv + streamingGuard 前端守护 + verify 脚本 - 知识注入 DRY/skills/audit 扩展 清理: - aichat 技术债(误报 allow/死导入/过时注释 30 项) - URGENT.md 删除(11 项加急全解决/迁 todo) - 文档整理(todo/待决策/待审查/ARCHITECTURE/INDEX + 总线/技术债审查新文档)
This commit is contained in:
@@ -7,7 +7,7 @@ use std::collections::HashSet;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::super::PendingApproval;
|
||||
use super::super::{ApprovalKind, PendingApproval};
|
||||
use super::risk_from_str;
|
||||
|
||||
/// 启动恢复:从审计表重建 pending_approvals(重启前未审批的工具调用,内存态已丢)
|
||||
@@ -64,19 +64,26 @@ pub async fn restore_pending_approvals(state: &AppState) {
|
||||
arguments: args,
|
||||
conversation_id: rec.conversation_id,
|
||||
recovered: true,
|
||||
// 阶段3a 单真相源合并:恢复的审批一律 kind=Risk(diff=None,与原顶层 diff 字段同语义)。
|
||||
//
|
||||
// **path 审批不恢复的决策(语义保留)**:路径授权挂起是会话级状态,重启后 session
|
||||
// 重建,无法恢复挂起语义;且 path 的"always"决策已写入持久白名单(Settings KV),
|
||||
// 重启后白名单仍生效(文件工具路径预校验会直接 Authorized,不再挂起)。故 path 审批
|
||||
// 无需恢复 —— 恢复 risk 即可覆盖所有需人工决策的积压。
|
||||
//
|
||||
// AE-2025-03: 重启恢复的审批不重读旧文件——审批可能跨重启,
|
||||
// 期间文件可能已被外部改动,重读生成 diff 反映的不是当初决策时的状态,
|
||||
// 且恢复路径在 session.lock 内做 async IO 复杂度高,预览价值低。
|
||||
// 前端见 diff=None 时回退显新 content。
|
||||
diff: None,
|
||||
// F-260619-03 Phase B: 重启恢复的审批一律视为普通 RiskLevel 审批(非路径授权挂起)。
|
||||
// 路径授权挂起是会话级状态,重启后 session 重建,无法恢复挂起语义。
|
||||
path_auth: None,
|
||||
kind: ApprovalKind::Risk { diff: None },
|
||||
// 阶段4:重启恢复的审批 retry_count=0(恢复语义即"待用户首次决策",非重试)。
|
||||
// 即便审计表已有 pending 记录,恢复后用户审批执行属首次正常执行,不断路。
|
||||
retry_count: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
tracing::info!(
|
||||
"启动恢复: {} 条 pending 工具审批重建到内存, 分布 {} 个 conv 的 per_conv",
|
||||
"启动恢复: {} 条 pending 工具审批重建到内存(pending_approvals), 分布 {} 个 conv 的 per_conv",
|
||||
session.pending_approvals.len(),
|
||||
convs_restored.len()
|
||||
);
|
||||
@@ -84,7 +91,7 @@ pub async fn restore_pending_approvals(state: &AppState) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests_f09_batch8_restore {
|
||||
use super::super::PendingApproval;
|
||||
use super::super::{ApprovalKind, PendingApproval};
|
||||
use super::*;
|
||||
use crate::commands::ai::AiSession;
|
||||
|
||||
@@ -114,7 +121,7 @@ mod tests_f09_batch8_restore {
|
||||
let _ = session.conv(cid); // 惰性建
|
||||
}
|
||||
}
|
||||
// pending 入单层 HashMap(不进 per_conv,设计 §2.1.2)。
|
||||
// 阶段3a 单真相源合并:恢复的 pending 全部进 pending_approvals,kind=Risk。
|
||||
session.pending_approvals.insert(
|
||||
tool_call_id.clone(),
|
||||
PendingApproval {
|
||||
@@ -123,8 +130,9 @@ mod tests_f09_batch8_restore {
|
||||
arguments: serde_json::Value::Null,
|
||||
conversation_id: conversation_id.clone(),
|
||||
recovered: true,
|
||||
diff: None,
|
||||
path_auth: None,
|
||||
kind: ApprovalKind::Risk { diff: None },
|
||||
// 阶段4:恢复的审批 retry_count=0(首次用户决策,非重试)。
|
||||
retry_count: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -138,8 +146,17 @@ mod tests_f09_batch8_restore {
|
||||
"未恢复的 conv 不应被惰性建"
|
||||
);
|
||||
|
||||
// 不变量 2:pending_approvals 单层 HashMap 4 条(含无主),conversation_id 保留(业务语义)。
|
||||
assert_eq!(session.pending_approvals.len(), 4, "全部 pending 入单层 HashMap");
|
||||
// 不变量 2:pending_approvals HashMap 4 条(含无主),conversation_id 保留(业务语义)。
|
||||
// 阶段3a:全部进单表 pending_approvals(原 risk_pending 合一,path 审批不恢复)。
|
||||
assert_eq!(session.pending_approvals.len(), 4, "全部 pending 入 pending_approvals");
|
||||
// 构造期恒等式:上方 pending_rows 构造的 4 条 PendingApproval 全部 kind=Risk(本测试构造
|
||||
// 时未插入任何 Path(_)),故 filter Path(_) 计数必为 0。这是构造恒等式而非外部不变量——
|
||||
// 对齐真实 restore_pending_approvals 的构造(实现 L78 一律 kind=Risk,无 Path 分支)。
|
||||
// 语义:首次 restore 无历史(Path 审批是会话级状态不恢复,恢复侧构造时本就不产出 Path)。
|
||||
let path_restored = session.pending_approvals.values()
|
||||
.filter(|a| matches!(a.kind, ApprovalKind::Path(_)))
|
||||
.count();
|
||||
assert_eq!(path_restored, 0, "构造期恒等式:恢复侧构造全部 kind=Risk,无 Path(首次 restore 无历史)");
|
||||
let conv_a_pending: Vec<_> = session
|
||||
.pending_approvals
|
||||
.values()
|
||||
@@ -157,7 +174,7 @@ mod tests_f09_batch8_restore {
|
||||
.values()
|
||||
.filter(|a| a.conversation_id.is_none())
|
||||
.collect();
|
||||
assert_eq!(ownerless.len(), 1, "无主审批 1 条仍入单层表(R-9)");
|
||||
assert_eq!(ownerless.len(), 1, "无主审批 1 条仍入 pending_approvals(R-9)");
|
||||
|
||||
// 不变量 3:同 conv 多条 pending 复用同一 PerConvState(conv() 幂等,不重复建)。
|
||||
let conv_a_ptr = session.conv("conv-a") as *const _;
|
||||
|
||||
Reference in New Issue
Block a user