新增: 批次工作落地(推进链/评估闭环/事件总线/并发/加固) + 技术债清理 + 文档整理
后端: - 工作流推进链(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:
@@ -23,8 +23,12 @@ pub fn run(conn: &Connection) -> Result<()> {
|
||||
|
||||
// 迁移步骤链: 顺序执行,跳过已应用的版本(current_version < N 才跑)。
|
||||
// 新增版本时,在此数组追加一项 (N, migrate_vN) 即可,无需改逻辑。
|
||||
// V20 = F-260619-01(任务关联灵感 idea_id);V21 = 消息拆分存储 + audit message_id。
|
||||
let steps: [(i32, fn(&Connection) -> Result<()>); 21] = [
|
||||
// V20 = F-260619-01(任务关联灵感 idea_id);V21 = 消息拆分存储 + audit message_id;
|
||||
// V22 = 灵感评估历史持久化(idea_evaluations 追加型审计表);
|
||||
// V23 = knowledges.embedding_status 列(嵌入失败可补偿重试);
|
||||
// V24 = ideas.related_ids 列(灵感间关联关系持久化打底);
|
||||
// V25 = idea_evaluations (idea_id, version) 唯一约束(评估版本并发重复兜底)。
|
||||
let steps: [(i32, fn(&Connection) -> Result<()>); 25] = [
|
||||
(1, migrate_v1),
|
||||
(2, migrate_v2),
|
||||
(3, migrate_v3),
|
||||
@@ -46,6 +50,10 @@ pub fn run(conn: &Connection) -> Result<()> {
|
||||
(19, migrate_v19),
|
||||
(20, migrate_v20),
|
||||
(21, migrate_v21),
|
||||
(22, migrate_v22),
|
||||
(23, migrate_v23),
|
||||
(24, migrate_v24),
|
||||
(25, migrate_v25),
|
||||
];
|
||||
|
||||
for (version, migrate_fn) in steps {
|
||||
@@ -512,6 +520,118 @@ fn migrate_v21(conn: &Connection) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V22:灵感评估历史持久化 — idea_evaluations 追加型审计表
|
||||
///
|
||||
/// 把灵感每次 AI 评估快照(ai_analysis / scores / score)按版本追加存表,
|
||||
/// 替代覆写 ideas.ai_analysis / ideas.scores 列。一条灵感多次评估产生多条记录,
|
||||
/// version 单调递增,前端按 (idea_id, version DESC) 取最新 + 翻历史。
|
||||
///
|
||||
/// 设计要点(对齐 knowledge_events 追加型审计表模式 V10):
|
||||
/// - **追加型**:只 INSERT 不 UPDATE,审计语义(评估快照不可篡改,历史可追溯)
|
||||
/// - **IF NOT EXISTS 幂等**:对新库建表 / 老库已有表跳过,均安全
|
||||
/// - **索引**:`(idea_id, version DESC)` 覆盖「取某灵感最新评估」最高频查询
|
||||
/// - **evaluated_by**:评估发起者(model 名 / human / system,可空)
|
||||
/// - **evaluated_at**:评估时间(毫秒字符串,同既有 model 约定)
|
||||
///
|
||||
/// 不登记通用列白名单(allowed_columns_for):本表走专用 list_by_idea,
|
||||
/// 宏生成的 query/update_field 未登记表会被 validate_column_name 保守拒绝
|
||||
/// (FR-S6),与追加型审计语义一致(历史不改),不开放通用写路径。
|
||||
fn migrate_v22(conn: &Connection) -> Result<()> {
|
||||
conn.execute_batch(V22_SQL)?;
|
||||
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [22])?;
|
||||
tracing::info!("迁移 v22 完成");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V23:幂等补 knowledges.embedding_status 列(嵌入生成失败可补偿重试)
|
||||
///
|
||||
/// P1 修复(嵌入失败无标记):spawn_embedding_for_knowledge 此前 fire-and-forget,
|
||||
/// provider 临时不可用 → 仅 warn,该条永久无向量索引但无人感知(下次也不会重试)。
|
||||
/// 新增 embedding_status 列跟踪嵌入生命周期:
|
||||
/// - NULL:未生成(老库行迁移后默认 NULL;代码从无显式写 NULL/pending 的路径,
|
||||
/// 仅此两种取值实际出现:done / failed)
|
||||
/// - done:成功(已有有效 embedding,由 KnowledgeRepo::set_embedding 写入)
|
||||
/// - failed:失败可重试(下次发布 / 手动 retry 时补偿,由 KnowledgeRepo::mark_embedding_failed 写入)
|
||||
///
|
||||
/// 语义:embedding 列(BLOB)与 embedding_status 解耦 —— embedding 仅在 done 时有值;
|
||||
/// 失败时 status=failed + embedding 仍 NULL,检索侧 `embedding IS NOT NULL` 自然跳过。
|
||||
/// 老库行(已成功嵌入的)embedding 有值但 status=NULL:这类条目检索正常(embedding IS NOT NULL),
|
||||
/// 不影响功能;若需精确状态,可在后台补偿脚本回填 done,但非必需(检索不依赖 status)。
|
||||
///
|
||||
/// TEXT NULL 向后兼容;不进通用 update_field 白名单(写入走 KnowledgeRepo::set_embedding /
|
||||
/// mark_embedding_failed 两个专用方法,而非独立的 set_embedding_status)。用 PRAGMA 探测列存在性,
|
||||
/// 缺失才 ALTER(同既有幂等模式),对新库/老库均安全。
|
||||
fn migrate_v23(conn: &Connection) -> Result<()> {
|
||||
if !column_exists(conn, "knowledges", "embedding_status") {
|
||||
conn.execute("ALTER TABLE knowledges ADD COLUMN embedding_status TEXT", [])?;
|
||||
tracing::info!("v23: 补建 knowledges.embedding_status 列(嵌入失败可补偿重试)");
|
||||
}
|
||||
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [23])?;
|
||||
tracing::info!("迁移 v23 完成");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V24:幂等补 ideas.related_ids 列(灵感间关联关系持久化打底)
|
||||
///
|
||||
/// 为灵感关联关系 UI 打底:related_ids 存「关联灵感 id JSON 数组」字符串
|
||||
/// (同 tags 的 JSON-in-TEXT 模式)。TEXT NULL 向后兼容:老库行默认 NULL,
|
||||
/// IdeaRecord 字段为 Option<String>(未设关联的灵感为 None)。
|
||||
///
|
||||
/// 进通用 update_field 白名单(Ideas.vue 关联关系 UI 走 update_idea →
|
||||
/// update_field('related_ids', ...),同 tags 一样白名单登记该列;另有整行
|
||||
/// update(update_full)路径,二者均可写入)。
|
||||
///
|
||||
/// 用 PRAGMA 探测列存在性,缺失才 ALTER(同 v4/v5/v6/v8/v10/v11/v14/v15/v16/v17
|
||||
/// /v18/v19/v20/v23 模式),对新库/老库均安全。
|
||||
fn migrate_v24(conn: &Connection) -> Result<()> {
|
||||
if !column_exists(conn, "ideas", "related_ids") {
|
||||
conn.execute("ALTER TABLE ideas ADD COLUMN related_ids TEXT", [])?;
|
||||
tracing::info!("v24: 补建 ideas.related_ids 列(灵感关联关系持久化打底)");
|
||||
}
|
||||
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [24])?;
|
||||
tracing::info!("迁移 v24 完成");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V25:幂等补 idea_evaluations (idea_id, version) 唯一约束(评估版本并发重复兜底)
|
||||
///
|
||||
/// 评估历史 version 此前由 evaluate_idea 算 `list_by_idea().first().version + 1`
|
||||
/// 得到,读-改-写非原子。并发两次评估同一灵感可能读到相同最新 version,各自 +1 后
|
||||
/// 写入相同 version(重复),破坏「version 单调递增 + 唯一」语义。单用户桌面应用
|
||||
/// 概率低,但唯一约束是数据完整性兜底,值得加。
|
||||
///
|
||||
/// 实现选 CREATE UNIQUE INDEX IF NOT EXISTS 而非 ALTER TABLE ADD CONSTRAINT:
|
||||
/// SQLite 不支持 ALTER TABLE 加约束 / 也不支持 ALTER ... IF NOT EXISTS,而
|
||||
/// `CREATE UNIQUE INDEX IF NOT EXISTS` 原生幂等(新库建 / 老库已有则跳过),满足
|
||||
/// 迁移「对新库与老库均安全」要求。索引语义等价于表级 UNIQUE(idea_id, version),
|
||||
/// 同样在 INSERT 冲突时抛 SQLITE_CONSTRAINT_UNIQUE。
|
||||
///
|
||||
/// 注:既有重复数据(若老库已有重复 version 行)会导致建索引失败。单用户桌面应用
|
||||
/// 几乎不会有重复,若真发生此处**降级跳过**(建索引失败 → warn + 继续迁移),而非 `?` 上抛
|
||||
/// 致整个应用启动崩溃、用户无感。理由:唯一索引只是并发重复的兜底防御网,缺失它不影响历史
|
||||
/// 数据读取(list_by_idea / list_recent_idea_evals 照常工作),应用仍可用,远胜启动失败黑屏。
|
||||
/// 建索引失败时日志带原始错误,用户/开发者可据此清理重复后手动重跑迁移补索引。
|
||||
fn migrate_v25(conn: &Connection) -> Result<()> {
|
||||
let build_result = conn.execute_batch(
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS uq_idea_evaluations_idea_version \
|
||||
ON idea_evaluations(idea_id, version)",
|
||||
);
|
||||
if let Err(e) = build_result {
|
||||
// 降级:建唯一索引失败(典型根因——老库已存在重复 (idea_id, version) 行)不阻断迁移。
|
||||
// 索引缺失仅削弱并发重复防御,不破坏既有数据可读性;跳过继续记录 schema_version=25。
|
||||
tracing::warn!(
|
||||
error = %e,
|
||||
"v25: 建 idea_evaluations(idea_id, version) 唯一索引失败(老库可能有重复 version 行),\
|
||||
降级跳过索引创建不阻断启动。清理重复后可手动重跑迁移补建索引"
|
||||
);
|
||||
} else {
|
||||
tracing::info!("v25: 建 idea_evaluations(idea_id, version) 唯一索引完成");
|
||||
}
|
||||
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [25])?;
|
||||
tracing::info!("迁移 v25 完成");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
|
||||
///
|
||||
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。
|
||||
@@ -537,6 +657,27 @@ CREATE TABLE IF NOT EXISTS ai_messages (
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_messages_conv ON ai_messages(conversation_id, seq);
|
||||
";
|
||||
|
||||
/// V22 建表 SQL — 灵感评估历史(追加型审计表)
|
||||
///
|
||||
/// 8 列:id(主键)/ idea_id(关联灵感)/ version(评估版本号,单调递增)/
|
||||
/// ai_analysis(AI 分析结果 JSON,可空)/ scores(多维评分 JSON,可空)/
|
||||
/// score(综合评分 REAL,可空)/ evaluated_by(评估者,可空)/ evaluated_at(毫秒字符串)。
|
||||
/// 索引:(idea_id, version DESC) 覆盖「取某灵感最新评估」最高频查询。
|
||||
const V22_SQL: &str = "
|
||||
CREATE TABLE IF NOT EXISTS idea_evaluations (
|
||||
id TEXT PRIMARY KEY,
|
||||
idea_id TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
ai_analysis TEXT,
|
||||
scores TEXT,
|
||||
score REAL,
|
||||
evaluated_by TEXT,
|
||||
evaluated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_idea_evaluations_idea ON idea_evaluations(idea_id, version DESC);
|
||||
";
|
||||
|
||||
/// V1 建表 SQL
|
||||
const V1_SQL: &str = "
|
||||
-- 想法表
|
||||
@@ -698,6 +839,9 @@ CREATE TABLE IF NOT EXISTS knowledges (
|
||||
verified INTEGER NOT NULL DEFAULT 0,
|
||||
source_project TEXT,
|
||||
source_ref TEXT,
|
||||
-- V23 补列(嵌入失败可补偿重试):新库直接带列,老库由 migrate_v23 ALTER 补;
|
||||
-- 两边列定义须一致(老库迁移注释 V23 已注明)。
|
||||
embedding_status TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user