新增: Phase2 阶段收尾(Sprint 1-20)
重构:删 5 零引用 crate(df-evolve/plugin/stages/task/traceability)+ 清死模块、ai.rs 拆 11 子 module、ai.ts 拆 6 composable、i18n 拆目录 功能:知识库全栈(df-project/scan + CRUD + 时间线 + 前端)、Settings 拆分、appSettings KV 迁移、模型池、LLM 并发 Semaphore 修复:审批持久化根治、ConditionEngine 默认拒绝、NodeRegistry unimplemented 清除、promote 补偿删除、工具结果截断 50KB、路径校验防 symlink 逃逸 文档:B-03 人工审批设计、决策记录三分档、规格契约自检、经验记录、todo 看板、PROGRESS 更新 详见 PROGRESS.md。src-tauri/儿童每日打卡应用/ 与本项目无关,已排除。
This commit is contained in:
@@ -10,9 +10,10 @@ pub struct ConditionEngine;
|
||||
impl ConditionEngine {
|
||||
/// 求值条件表达式
|
||||
///
|
||||
/// TODO: 实现完整的表达式解析,当前仅支持简单的 JSON Path 比较
|
||||
/// 当前仅支持 "true"/"false" 字面量。未识别的表达式 **默认 false**(保守拒绝),
|
||||
/// 而非默认 true——条件分支写错或引擎未实现时不应该静默放行。
|
||||
/// TODO: 实现完整的表达式解析(JSON Path / 数值比较 / contains / 逻辑组合)。
|
||||
pub fn evaluate(expr: &str, _context: &Value) -> anyhow::Result<bool> {
|
||||
// 骨架:简单的 true/false 字面量
|
||||
let trimmed = expr.trim();
|
||||
if trimmed == "true" {
|
||||
return Ok(true);
|
||||
@@ -27,7 +28,70 @@ impl ConditionEngine {
|
||||
// - "$.tags contains 'ai'" — 包含检查
|
||||
// - "and/or/not" — 逻辑组合
|
||||
|
||||
tracing::warn!("条件表达式引擎尚未完整实现,表达式: {}", expr);
|
||||
Ok(true)
|
||||
tracing::warn!("条件表达式引擎尚未完整实现,表达式未识别默认 false: {}", expr);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn ctx() -> Value {
|
||||
// 当前实现未使用 context,但保持传入以对齐签名
|
||||
json!({})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_true_literal_returns_true() {
|
||||
assert_eq!(ConditionEngine::evaluate("true", &ctx()).unwrap(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_false_literal_returns_false() {
|
||||
assert_eq!(ConditionEngine::evaluate("false", &ctx()).unwrap(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unsupported_expression_defaults_to_false() {
|
||||
// 尚未实现的语法:表达式非 true/false 字面量时默认 false(保守拒绝,不静默放行)
|
||||
assert_eq!(
|
||||
ConditionEngine::evaluate("$.status == 'completed'", &ctx()).unwrap(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whitespace_is_trimmed() {
|
||||
// trimmed() 去除首尾空白后再与字面量比较
|
||||
assert_eq!(
|
||||
ConditionEngine::evaluate(" true ", &ctx()).unwrap(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
ConditionEngine::evaluate("\tfalse\n", &ctx()).unwrap(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_string_defaults_to_false() {
|
||||
assert_eq!(ConditionEngine::evaluate("", &ctx()).unwrap(), false);
|
||||
assert_eq!(ConditionEngine::evaluate(" ", &ctx()).unwrap(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_case_sensitive_not_matched() {
|
||||
// 字面量比较区分大小写:True/False 既非 "true" 也非 "false",默认 false
|
||||
assert_eq!(ConditionEngine::evaluate("True", &ctx()).unwrap(), false);
|
||||
assert_eq!(ConditionEngine::evaluate("FALSE", &ctx()).unwrap(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbitrary_string_defaults_to_false() {
|
||||
assert_eq!(ConditionEngine::evaluate("yes", &ctx()).unwrap(), false);
|
||||
assert_eq!(ConditionEngine::evaluate("1", &ctx()).unwrap(), false);
|
||||
assert_eq!(ConditionEngine::evaluate("completed", &ctx()).unwrap(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +74,6 @@ impl NodeRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NodeRegistry {
|
||||
fn default() -> Self {
|
||||
let mut registry = Self::new();
|
||||
registry.register("script", |_config| {
|
||||
// ScriptNode 的工厂 — 需要 df-nodes 依赖后才可用
|
||||
// 这里返回一个占位实现,实际项目中由 df-nodes crate 注册
|
||||
unimplemented!("ScriptNode 需要通过 df-nodes 注册")
|
||||
});
|
||||
registry
|
||||
}
|
||||
}
|
||||
// 不实现 Default:原 Default 注册了一个会 panic 的 script 工厂(unimplemented!),
|
||||
// 违反项目铁律「无 panic——所有占位代码返回空/默认值」。所有调用方应显式 new() +
|
||||
// 手动 register 真实节点(如 state.rs::build_registry 的做法)。
|
||||
|
||||
Reference in New Issue
Block a user