修复: MCP list_tasks 默认排除终态 + read_skill 隔离围栏

- list_tasks 无 status 过滤时默认排除 done/cancelled, 终态存量(260+条)挤占分页窗口致活跃任务被挤出视图; 显式传 status 不干预
- df-storage TaskQuery 新增 status_not_in 字段 + SQL NOT IN 下推(占位符先拼后绑, 防序号/值错位)
- read_skill 返回内容外包 skill_documentation XML 围栏+隔离声明, 防技能文档内容与系统指令混淆
- df-mcp 64 passed(含新增终态排除测试)
This commit is contained in:
lxy
2026-10-06 23:48:11 +08:00
parent 9ff26f0c8f
commit 9520c198a2
3 changed files with 77 additions and 2 deletions
+21
View File
@@ -100,6 +100,10 @@ pub struct TaskQuery {
pub project_id: Option<String>,
/// 状态过滤(P1 下沉,命中 idx_tasks_status)。值集由上层 TaskStatus::is_valid 兜底校验。
pub status: Option<String>,
/// 状态排除集(1eff911c 缺陷修复,MCP list_tasks 无 status 过滤时默认排除
/// done/cancelled 终态,防存量终态任务挤占分页窗口)。与 status 互斥使用:
/// 同传时 status 等值优先,status_not_in 忽略(SQL 拼接顺序 status 先判)。
pub status_not_in: Option<Vec<String>>,
/// 优先级过滤(0=critical..3=low)。P3 基建就绪,视图暂不用。
pub priority: Option<i32>,
/// 负责人过滤。P3 基建就绪,视图暂不用。
@@ -390,6 +394,7 @@ impl TaskRepo {
// 拷贝 query 字段进闭包('static 生命周期,spawn_blocking 要求)
let project_id = query.project_id.clone();
let status = query.status.clone();
let status_not_in = query.status_not_in.clone();
let priority = query.priority;
let assignee = query.assignee.clone();
let keyword = query.keyword.clone();
@@ -417,6 +422,22 @@ impl TaskRepo {
where_clauses.push(format!("status = ?{}", params_vec.len() + 1));
params_vec.push(Box::new(s.clone()));
}
// status_not_in(1eff911c 修复):状态排除集,先拼占位符再统一绑值(两步分离,
// 避免边拼边绑的序号/值错位)。与 status 同传时 status 等值优先(互斥由调用方保证)。
if status.is_none() {
if let Some(excl) = &status_not_in {
if !excl.is_empty() {
let base = params_vec.len();
let placeholders: Vec<String> = (0..excl.len())
.map(|i| format!("?{}", base + i + 1))
.collect();
for v in excl {
params_vec.push(Box::new(v.clone()));
}
where_clauses.push(format!("status NOT IN ({})", placeholders.join(", ")));
}
}
}
if let Some(p) = priority {
where_clauses.push(format!("priority = ?{}", params_vec.len() + 1));
params_vec.push(Box::new(p));