新增: 知识图谱Phase1数据层(父②) + 修复 idea list_by_query 占位符(父⑤⑤.1)

父② 数据层(V29):
- migrate_v29: tasks +queue/parent_id/content_json + task_links 表+双索引(幂等对标 v14/v24)
- TaskRecord 加 3 字段(15→18 列,INSERT/SELECT×6/from_row/struct 同步)+ 8 构造点补默认
- TaskLinkRepo(新文件,CRUD + 循环依赖 BFS depends_on 链检测,14 测)
- task_repo list_by_query queue/parent_id 筛选 + get_children + 聚合计数(10 测)
- queue DEFAULT 'todo' 向后兼容;task_links 软删保留(无 ON DELETE)

idea_repo list_by_query 占位符修复(父⑤⑤.1 引入的潜伏 bug):
- 加 deleted_at IS NULL 恒带后 where_clauses.len()+1 偏移 → 非空 status/keyword 查询 rusqlite 错误
- 改 params_vec.len()+1(对标 task_repo ②.2 同款修复,父② agent 发现)

df-storage 96 lib + 11 集成测试全过。
This commit is contained in:
2026-06-26 22:50:48 +08:00
parent 549f198cea
commit df8ed7e74f
13 changed files with 1082 additions and 23 deletions

View File

@@ -33,7 +33,11 @@ pub fn run(conn: &Connection) -> Result<()> {
// 对齐 DTO 契约 audit/mod.rs:53 只列 completed + 前端 i18n auditLog.status 无 executed 键 +
// 治 AuditLog executed 记录显示错位蓝pending标签+raw"executed")。
// V28 = 灵感软删回收站(ideas.deleted_at,对标 tasks.deleted_at V14/projects.deleted_at V11)。
let steps: [(i32, fn(&Connection) -> Result<()>); 28] = [
// V29 = 知识图谱 Phase 1 任务网络基础(对标 docs/02-架构设计/专项设计/
// 项目知识图谱与任务队列系统-2026-06-26.md §2.1/§2.2):tasks 加 queue(管理池)/
// parent_id(父任务纵向关联)/content_json(结构化需求规格)三列 + task_links 表
// (横向关联 depends_on/blocks/relates_to),为 AI 编排地基打底。
let steps: [(i32, fn(&Connection) -> Result<()>); 29] = [
(1, migrate_v1),
(2, migrate_v2),
(3, migrate_v3),
@@ -62,6 +66,7 @@ pub fn run(conn: &Connection) -> Result<()> {
(26, migrate_v26),
(27, migrate_v27),
(28, migrate_v28),
(29, migrate_v29),
];
for (version, migrate_fn) in steps {
@@ -698,6 +703,76 @@ fn migrate_v28(conn: &Connection) -> Result<()> {
Ok(())
}
/// V29:知识图谱 Phase 1 任务网络基础 — 幂等补 tasks.queue / parent_id / content_json 三列
///
/// 对标 docs/02-架构设计/专项设计/项目知识图谱与任务队列系统-2026-06-26.md §2.1,
/// 为「AI 拥有完整项目知识图谱、自主分解任务编排依赖」打数据层地基。三列各司其职:
///
/// - `queue TEXT NOT NULL DEFAULT 'todo'`:管理维度池标记,与 status(执行维度)正交。
/// 取值 backlog(需求池)/todo(待办池)/decision(待决策池)/active(执行中)/done(已完成)。
/// DEFAULT 'todo' 保证老任务迁移后取值确定(非 NULL),向后兼容:历史任务原 status=todo,
/// 落 todo 池语义一致。queue 与 status 一致性约束由 IPC 层校验(不进状态机,不进 DB 约束)。
///
/// - `parent_id TEXT REFERENCES tasks(id)`:父任务纵向关联(AI 分解-执行编排结构)。
/// NULL = 叶子任务(走状态机 advance_task);非空 = 子任务。
/// 限制 1 级嵌套(无孙任务)由 IPC 层校验,不进 DB 约束。父任务=容器模型,status 由
/// 子任务聚合计算(不走状态机)。TEXT NULL 向后兼容(老任务无 parent → None)。
///
/// - `content_json TEXT`:结构化需求规格 JSON 字符串(AI 可读写的执行规格)。
/// 结构 { background, acceptance_criteria[], scope[], technical_design, custom_fields }。
/// AI 从对话提取填充,执行中用 acceptance_criteria 自检。NULL = 无结构化规格(纯文本 description)。
/// TEXT NULL 向后兼容(老任务无 content_json → None)。
///
/// 对标 v14(v14 tasks.deleted_at)/ v24(v24 ideas.related_ids)幂等模式:每列用 PRAGMA
/// 探测存在性,缺失才 ALTER,对新库/老库/坏库均安全(列已存在时跳过 ALTER 不报 duplicate column)。
fn migrate_v29(conn: &Connection) -> Result<()> {
if !column_exists(conn, "tasks", "queue") {
conn.execute(
"ALTER TABLE tasks ADD COLUMN queue TEXT NOT NULL DEFAULT 'todo'",
[],
)?;
tracing::info!("v29: 补建 tasks.queue 列(管理池,知识图谱 Phase 1)");
}
if !column_exists(conn, "tasks", "parent_id") {
conn.execute(
"ALTER TABLE tasks ADD COLUMN parent_id TEXT REFERENCES tasks(id)",
[],
)?;
tracing::info!("v29: 补建 tasks.parent_id 列(父任务纵向关联,知识图谱 Phase 1)");
}
if !column_exists(conn, "tasks", "content_json") {
conn.execute("ALTER TABLE tasks ADD COLUMN content_json TEXT", [])?;
tracing::info!("v29: 补建 tasks.content_json 列(结构化需求规格,知识图谱 Phase 1)");
}
// task_links 表(横向关联,对标设计 §2.2):depends_on/blocks/relates_to。
// CREATE TABLE IF NOT EXISTS 幂等:新库建、老库(V29 之前的库已跑过前半三列)已有则跳过。
// 循环依赖检测走应用层(TaskLinkRepo::create_link BFS),非 DB 约束(对标设计 D8)。
// 软删除语义:Task 软删不级联删 link(恢复后关系还在),故无 ON DELETE,FK 仅引用完整性。
conn.execute(
"CREATE TABLE IF NOT EXISTS task_links (
id TEXT PRIMARY KEY,
source_id TEXT NOT NULL REFERENCES tasks(id),
target_id TEXT NOT NULL REFERENCES tasks(id),
link_type TEXT NOT NULL,
remark TEXT,
created_at TEXT NOT NULL
)",
[],
)?;
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_task_links_source ON task_links(source_id)",
[],
)?;
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_task_links_target ON task_links(target_id)",
[],
)?;
tracing::info!("v29: 建 task_links 表 + 索引(任务横向关联,知识图谱 Phase 1)");
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [29])?;
tracing::info!("迁移 v29 完成");
Ok(())
}
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
///
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。