feat(mcp/diagnostics): get_diagnostics 新增使者回传覆盖率「沉默项目」信号(B4)
All checks were successful
Push & PR Check / check (push) Successful in 49s
All checks were successful
Push & PR Check / check (push) Successful in 49s
近 30 天有 commit 采集但零使者回传的已登记项目浮出水面, 提示去该项目跑 /envoy——回流触发不自动化(辩证决策 2026-07-15), 用可观测性让遗忘可见,人留在过滤环里。 Rules-Applied: R06,R07
This commit is contained in:
parent
aaf3343133
commit
2fe576b75a
@ -372,7 +372,7 @@ modules:
|
||||
area: backend
|
||||
status: in_progress
|
||||
progress: 99
|
||||
note: "体系分发主线:T1/T2/B1/B2/B3/V1/U3 ✅(U3 2026-07-15:Rules-Applied ingest 层校验+回溯清洗);剩 📋 U1(全托管升级通道+批量重分发)/ B4(遵守度+回传覆盖率,吸收被否决的 U2)"
|
||||
note: "体系分发主线:T1/T2/B1/B2/B3/V1/U3/B4 ✅(2026-07-15:U3 Rules-Applied ingest 层校验+回溯清洗;B4 沉默项目回传覆盖率诊断);剩 📋 U1(全托管升级通道+批量重分发)"
|
||||
position: [3500, 0]
|
||||
updated: 2026-07-15
|
||||
|
||||
|
||||
@ -134,7 +134,7 @@
|
||||
- 版本化闭环要求(2026-07-03 用户澄清,体系分发的灵魂): 分发不是"发了就不管"——每类资产带版本分发 → 子项目按该版本实践产生经验(成功/失败)→ 经验自吸反哺炼境(trailer/failure_cases/复盘/返工率)→ 按版本分组对比效果 → 修订出新版 → 再分发。CONVENTIONS 是模范生(version+队列对比+梦核+同步,1.8→1.9 已跑通一整圈);B 系列每类资产按此标准建闭环。**前置依赖提升**:「接入包模板演进分发(MANAGED 块化)」concept 卡——模板无版本机制是当前最大缺口,没有它体系 v2 到不了存量项目
|
||||
- notes: 2026-07-03 概念探讨收束后的分发需求(体系详见 memory concept_dual_projection 与 mcp-server 模块 concept 卡)。用户全 agent 开发形态下,"给开发者立规范"="给 agent 注入规范",可完全机器化。四条现成管道分发四类资产:① B1(S) CONVENTIONS 1.10.0 加 R12 边界单点实现 + R13 验收须真实走通验证路径 + 载体选择原则;② B2(S) 接入包 AGENTS.md 模板加 agent 按钮章节(三要素/载体谱系/侦察驻军);③ B3(M) init/sync 提示词升级——建蓝图时声明项目按钮载体、acceptance 生成为可执行验证路径格式;④ B4(S,后置) 健康诊断加体系遵守度检查。移植体验=零新增动作,接入流程不变。
|
||||
|
||||
### 📋 U1. 全托管文件升级通道 + 批量重分发(择升环断点修复 ①)
|
||||
### 🔵 U1. 全托管文件升级通道 + 批量重分发(择升环断点修复 ①)
|
||||
- status: planned
|
||||
- complexity: M
|
||||
- depends: onboarding-pack(卡 T1)
|
||||
@ -150,8 +150,9 @@
|
||||
- acceptance: [cargo test] trailer 解析校验纯函数——规则 ID 不存在于该项目 .blueprint/CONVENTIONS.md(如 R99)→ 从 rules_applied 剔除并可观测(日志或 invalid 计数),真实 ID 保留;项目无 CONVENTIONS.md → 不校验全保留;[MCP tool] ingest_git_history 重跑 → 「规则命中 × 返工」交叉统计不再含未知 ID(存量污染可回溯清洗)
|
||||
- notes: 辩证翻转载体:原方案(子项目 commit-msg hook)违反 R12 边界单点——统计消费端在炼境,校验就该在炼境 ingest 层。收益:零分发成本覆盖全部项目、重跑即回溯清洗、cargo test 可测(炼境自身 agent 按钮载体)。commit-msg hook 即时反馈版降为可选增强(若做依赖 U1 通道,不在本卡)。上限诚实:只挡 typo/幻觉 ID,「真 ID 假触发」留给宁缺毋滥纪律
|
||||
|
||||
### 📋 B4. 健康诊断遵守度 + 回传覆盖率(吸收被否决的 U2,择升环断点修复 ③)
|
||||
- status: planned
|
||||
### ✅ B4. 健康诊断遵守度 + 回传覆盖率(吸收被否决的 U2,择升环断点修复 ③)
|
||||
- status: done
|
||||
- done_note: 2026-07-15 完成。onboarding.rs 新增 silent_projects(conn)(R06 注入):近 30 天有 commit_metrics 但零使者回传的已登记项目,SQL 经 blueprint_buffs UUID→win_path→project_workspaces 双路径关联(复用 get_all_commit_health 身份解析形状),未登记项目不列入(无法跑 /envoy);get_diagnostics 新增「使者回传覆盖率」段,沉默项目列名称+commit 数并提示跑 /envoy。cargo test 99 全过(新测覆盖:UUID 关联命名/普通笔记不算回传/未登记不列入/回传过期重新变沉默)
|
||||
- complexity: S
|
||||
- files: src-tauri/src/mcp_tools.rs, src-tauri/src/commands/onboarding.rs
|
||||
- acceptance: [MCP tool] get_diagnostics(或 verify_onboarding 检查链)新增「沉默项目」信号:项目近 30 天 commit_metrics 有数据但使者回传(list_envoy_returns 同源数据)为 0 → 列出项目名 + commit 数,提示去该项目跑 /envoy;近 30 天有回传或无 commit 活动 → 不报
|
||||
|
||||
@ -408,6 +408,43 @@ pub fn verify_onboarding(
|
||||
checks
|
||||
}
|
||||
|
||||
// ── B4:使者回传覆盖率(「沉默项目」信号)──────────────────────────────────────
|
||||
|
||||
/// 「沉默项目」:近 30 天有 commit 采集但零使者回传的已登记项目(名称, commit 数)。
|
||||
///
|
||||
/// 回流触发不自动化(决策 2026-07-15):不在子项目装 hook 催回传,
|
||||
/// 而是让遗忘可被看见——人看到信号后自行决定是否去该项目跑 /envoy。
|
||||
/// commit_metrics.project_id 可能是 blueprint_buffs 的 UUID,经 buff 路径
|
||||
/// 关联回 project_workspaces;未登记项目(无法跑 /envoy)不列入。
|
||||
pub fn silent_projects(conn: &rusqlite::Connection) -> Result<Vec<(String, i64)>, String> {
|
||||
let mut stmt = conn
|
||||
.prepare(
|
||||
"SELECT
|
||||
COALESCE(pp.name, pw.id) AS name,
|
||||
COUNT(*) AS commits
|
||||
FROM commit_metrics cm
|
||||
LEFT JOIN blueprint_buffs bb ON bb.project_id = cm.project_id
|
||||
LEFT JOIN project_workspaces pw
|
||||
ON pw.id = cm.project_id OR pw.win_path = bb.project_path
|
||||
LEFT JOIN project_profiles pp ON pw.profile_id = pp.id
|
||||
WHERE cm.committed_at >= datetime('now','-30 days')
|
||||
AND pw.id IS NOT NULL
|
||||
GROUP BY pw.id
|
||||
HAVING (SELECT COUNT(*) FROM project_notes pn
|
||||
WHERE pn.project_id = pw.id
|
||||
AND pn.content LIKE '【炼境使者回传】%'
|
||||
AND pn.created_at >= datetime('now','localtime','-30 days')) = 0
|
||||
ORDER BY commits DESC",
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rows = stmt
|
||||
.query_map([], |r| Ok((r.get::<_, String>(0)?, r.get::<_, i64>(1)?)))
|
||||
.map_err(|e| e.to_string())?
|
||||
.filter_map(|r| r.ok())
|
||||
.collect();
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -602,5 +639,62 @@ mod tests {
|
||||
assert!(!check_managed_version(None, expected).0, "文件缺失应 fail");
|
||||
assert!(!check_managed_version(Some("手写内容,无标记"), expected).0, "无标记应 fail");
|
||||
}
|
||||
|
||||
// ── B4:使者回传覆盖率(沉默项目信号)────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn silent_projects_flags_active_without_returns() {
|
||||
let conn = crate::db::conn_with_schema();
|
||||
// 两个已登记项目:pw-a 有 commit 无回传(沉默);pw-b 有 commit 有回传
|
||||
conn.execute_batch(
|
||||
"INSERT INTO project_profiles (id, name) VALUES ('prof-a','项目A'),('prof-b','项目B');
|
||||
INSERT INTO project_workspaces (id, profile_id, win_path)
|
||||
VALUES ('pw-a','prof-a','E:/a'),('pw-b','prof-b','E:/b');",
|
||||
)
|
||||
.unwrap();
|
||||
// pw-a 的 commit 走 blueprint_buffs UUID 关联(生产真实形状)
|
||||
conn.execute(
|
||||
"INSERT INTO blueprint_buffs (project_id, project_path) VALUES ('uuid-a','E:/a')",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
conn.execute(
|
||||
"INSERT INTO commit_metrics (sha, project_id, commit_type, is_rework, is_ci_auto, committed_at, parsed_at)
|
||||
VALUES ('c1','uuid-a','feat',0,0,datetime('now'),datetime('now')),
|
||||
('c2','uuid-a','fix',1,0,datetime('now'),datetime('now')),
|
||||
('c3','pw-b','feat',0,0,datetime('now'),datetime('now'))",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
// pw-b 近 30 天有使者回传;pw-a 只有普通笔记(不算回传)
|
||||
conn.execute(
|
||||
"INSERT INTO project_notes (project_id, content) VALUES
|
||||
('pw-b','【炼境使者回传】来源: 项目B ...'),
|
||||
('pw-a','普通笔记,不是回传')",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
// 未登记项目的 commit 不列入(无法跑 /envoy)
|
||||
conn.execute(
|
||||
"INSERT INTO commit_metrics (sha, project_id, commit_type, is_rework, is_ci_auto, committed_at, parsed_at)
|
||||
VALUES ('c9','orphan','feat',0,0,datetime('now'),datetime('now'))",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let silent = silent_projects(&conn).unwrap();
|
||||
assert_eq!(silent.len(), 1, "只有 pw-a 沉默:{silent:?}");
|
||||
assert_eq!(silent[0].0, "项目A", "经 buff UUID 关联解析出名称");
|
||||
assert_eq!(silent[0].1, 2, "commit 数按项目聚合");
|
||||
|
||||
// 老回传(>30 天)不算覆盖:把 pw-b 的回传改旧后应变沉默
|
||||
conn.execute(
|
||||
"UPDATE project_notes SET created_at = datetime('now','localtime','-40 days') WHERE project_id = 'pw-b'",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
let silent = silent_projects(&conn).unwrap();
|
||||
assert_eq!(silent.len(), 2, "pw-b 的回传过期后也应列为沉默");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -752,6 +752,20 @@ fn get_diagnostics() -> Result<String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
out.push_str("\n### 使者回传覆盖率(近 30 天)\n\n");
|
||||
match crate::commands::onboarding::silent_projects(&conn) {
|
||||
Ok(list) if list.is_empty() => {
|
||||
out.push_str("_无沉默项目:有 commit 活动的已接入项目均有使者回传(或近期无活动)_ ✅\n");
|
||||
}
|
||||
Ok(list) => {
|
||||
out.push_str("以下项目近 30 天有 commit 活动但 **0 条使者回传**——环的回流段在这些项目上是断的,考虑去该项目会话跑一次 `/envoy`:\n\n");
|
||||
for (name, commits) in &list {
|
||||
out.push_str(&format!("- **{}** — {} 条 commit,0 回传\n", name, commits));
|
||||
}
|
||||
}
|
||||
Err(e) => out.push_str(&format!("_查询失败: {e}_\n")),
|
||||
}
|
||||
|
||||
out.push_str("\n### 系统健康\n\n");
|
||||
let report = crate::commands::settings::collect_health_report();
|
||||
out.push_str(&format!("检查时间:{}\n\n", report.checked_at));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user