refactor: 蓝图系统三项优化(P1/P3/P4)
P1 - CONVENTIONS 版本号自动同步 - 删除手动维护的 MASTER_CONVENTIONS_VERSION 常量 - 新增 master_conventions_version() 从 frontmatter 自动解析,基于 OnceLock 缓存 - CLAUDE_MANAGED_TEMPLATE 改为 claude_managed_template() 函数,版本号随之自动同步 P3 - 梦核提示词精简 - 新增 conventions_rules_summary(),按白名单抽取规则段落 - 保留:何时更新蓝图、模块状态定义、可派发标准、迭代管理、飞轮外环、注意事项 - 跳过:任务卡格式、manifest 字段规范、流程说明等实现细节 - 提示词 CONVENTIONS 部分从 ~500 行压缩至关键规则段落 P4 - usage.json 补执行 delta - 快照新增 task_ids.done / task_ids.blocked(当前任务状态,供下次对比) - 快照新增 delta.newly_done / newly_blocked / unblocked(与上次快照的差量) - 梦核可据此看到执行轨迹,而非仅状态截面
This commit is contained in:
parent
69c05380cc
commit
4d9c003470
@ -4,10 +4,24 @@ use std::path::Path;
|
||||
// ── 主节点:master CONVENTIONS.md 内容和版本(编译时嵌入)────────────────────
|
||||
|
||||
const MASTER_CONVENTIONS: &str = include_str!("../../../.blueprint/CONVENTIONS.md");
|
||||
const MASTER_CONVENTIONS_VERSION: &str = "1.4.2";
|
||||
|
||||
// CLAUDE.md 蓝图管理区模板(版本与 CONVENTIONS 保持一致)
|
||||
const CLAUDE_MANAGED_TEMPLATE: &str = r#"<!-- BLUEPRINT_MANAGED_START version="1.4.2" -->
|
||||
/// 从 MASTER_CONVENTIONS frontmatter 自动解析版本号,无需手动维护常量。
|
||||
fn master_conventions_version() -> &'static str {
|
||||
static VERSION: std::sync::OnceLock<String> = std::sync::OnceLock::new();
|
||||
VERSION.get_or_init(|| {
|
||||
for line in MASTER_CONVENTIONS.lines() {
|
||||
if let Some(v) = line.strip_prefix("version: ") {
|
||||
return v.trim().to_string();
|
||||
}
|
||||
}
|
||||
"unknown".to_string()
|
||||
})
|
||||
}
|
||||
|
||||
/// CLAUDE.md 蓝图管理区模板,版本号从 CONVENTIONS frontmatter 自动同步。
|
||||
fn claude_managed_template() -> String {
|
||||
let ver = master_conventions_version();
|
||||
format!(r#"<!-- BLUEPRINT_MANAGED_START version="{ver}" -->
|
||||
## 项目蓝图
|
||||
|
||||
本项目使用 `.blueprint/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
|
||||
@ -30,7 +44,8 @@ const CLAUDE_MANAGED_TEMPLATE: &str = r#"<!-- BLUEPRINT_MANAGED_START version="1
|
||||
详见 `.blueprint/CONVENTIONS.md`,核心规则:
|
||||
- 任务卡同时具备 `files` + `acceptance` + complexity `S/M` → 标记为 📋 可派发
|
||||
- 可派发的任务卡可以直接交给 Claude Agent 独立完成
|
||||
<!-- BLUEPRINT_MANAGED_END -->"#;
|
||||
<!-- BLUEPRINT_MANAGED_END -->"#)
|
||||
}
|
||||
|
||||
// ── manifest.yaml 对应结构 ───────────────────────────────────────────────────
|
||||
|
||||
@ -896,7 +911,7 @@ pub fn get_blueprint_status(project_path: String) -> Result<BlueprintStatus, Str
|
||||
claude_md_managed: false,
|
||||
claude_md_version: None,
|
||||
manifest_updated: None,
|
||||
master_version: MASTER_CONVENTIONS_VERSION.to_string(),
|
||||
master_version: master_conventions_version().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -920,9 +935,9 @@ pub fn get_blueprint_status(project_path: String) -> Result<BlueprintStatus, Str
|
||||
let manifest_updated = read_manifest_updated(&bp_dir.join("manifest.yaml"));
|
||||
|
||||
// 判断状态
|
||||
let rules_current = conventions_version.as_deref() == Some(MASTER_CONVENTIONS_VERSION)
|
||||
let rules_current = conventions_version.as_deref() == Some(master_conventions_version())
|
||||
&& claude_md_managed
|
||||
&& claude_md_version.as_deref() == Some(MASTER_CONVENTIONS_VERSION);
|
||||
&& claude_md_version.as_deref() == Some(master_conventions_version());
|
||||
|
||||
let status = if !rules_current {
|
||||
"rules_outdated".to_string()
|
||||
@ -938,7 +953,7 @@ pub fn get_blueprint_status(project_path: String) -> Result<BlueprintStatus, Str
|
||||
claude_md_managed,
|
||||
claude_md_version,
|
||||
manifest_updated,
|
||||
master_version: MASTER_CONVENTIONS_VERSION.to_string(),
|
||||
master_version: master_conventions_version().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -965,9 +980,10 @@ pub fn sync_blueprint_rules(project_path: String) -> Result<SyncResult, String>
|
||||
|
||||
// 2. 处理 CLAUDE.md
|
||||
let claude_path = root.join("CLAUDE.md");
|
||||
let managed_template = claude_managed_template();
|
||||
let claude_action = if !claude_path.exists() {
|
||||
// 创建新文件
|
||||
let content = format!("# {}\n\n{CLAUDE_MANAGED_TEMPLATE}\n",
|
||||
let content = format!("# {}\n\n{managed_template}\n",
|
||||
root.file_name().and_then(|n| n.to_str()).unwrap_or("Project"));
|
||||
std::fs::write(&claude_path, content)
|
||||
.map_err(|e| format!("创建 CLAUDE.md 失败: {e}"))?;
|
||||
@ -978,13 +994,13 @@ pub fn sync_blueprint_rules(project_path: String) -> Result<SyncResult, String>
|
||||
|
||||
if content.contains("<!-- BLUEPRINT_MANAGED_START") {
|
||||
// 替换现有 MANAGED 区
|
||||
let new_content = replace_managed_section(&content, CLAUDE_MANAGED_TEMPLATE);
|
||||
let new_content = replace_managed_section(&content, &managed_template);
|
||||
std::fs::write(&claude_path, new_content)
|
||||
.map_err(|e| format!("更新 CLAUDE.md 失败: {e}"))?;
|
||||
"updated".to_string()
|
||||
} else {
|
||||
// 追加 MANAGED 区
|
||||
let new_content = format!("{}\n\n{CLAUDE_MANAGED_TEMPLATE}\n", content.trim_end());
|
||||
let new_content = format!("{}\n\n{managed_template}\n", content.trim_end());
|
||||
std::fs::write(&claude_path, new_content)
|
||||
.map_err(|e| format!("追加 CLAUDE.md 失败: {e}"))?;
|
||||
"appended".to_string()
|
||||
@ -995,7 +1011,7 @@ pub fn sync_blueprint_rules(project_path: String) -> Result<SyncResult, String>
|
||||
success: true,
|
||||
conventions_written: true,
|
||||
claude_md_action: claude_action.clone(),
|
||||
message: format!("CONVENTIONS.md 已同步至 v{MASTER_CONVENTIONS_VERSION},CLAUDE.md {claude_action}"),
|
||||
message: format!("CONVENTIONS.md 已同步至 v{},CLAUDE.md {claude_action}", master_conventions_version()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -1348,6 +1364,45 @@ pub fn get_flywheel_stats(project_paths: Vec<String>) -> Result<FlywheelStats, S
|
||||
|
||||
// ── 梦核提示词生成 ────────────────────────────────────────────────────────────
|
||||
|
||||
/// 从完整 CONVENTIONS 中抽取对梦核有判断价值的规则段落。
|
||||
/// 跳过:任务卡格式定义、manifest 字段规范、安装说明、/architect /splitter 流程等纯实现细节。
|
||||
fn conventions_rules_summary() -> String {
|
||||
// 保留这些二级/三级标题对应的章节(前缀匹配)
|
||||
const KEEP: &[&str] = &[
|
||||
"## 何时更新蓝图",
|
||||
"## 模块状态定义",
|
||||
"### 可派发标准",
|
||||
"## 迭代管理",
|
||||
"## 飞轮外环",
|
||||
"## 注意事项",
|
||||
];
|
||||
|
||||
let mut result = Vec::<&str>::new();
|
||||
let mut in_keep = false;
|
||||
let mut current_section_start = 0usize;
|
||||
let lines: Vec<&str> = MASTER_CONVENTIONS.lines().collect();
|
||||
|
||||
for (i, &line) in lines.iter().enumerate() {
|
||||
let is_heading = line.starts_with("## ") || line.starts_with("### ");
|
||||
if is_heading {
|
||||
if in_keep {
|
||||
// 结束上一个保留段
|
||||
let chunk = &lines[current_section_start..i];
|
||||
result.extend_from_slice(chunk);
|
||||
result.push("");
|
||||
}
|
||||
in_keep = KEEP.iter().any(|&k| line.starts_with(k));
|
||||
if in_keep { current_section_start = i; }
|
||||
}
|
||||
}
|
||||
// 收尾
|
||||
if in_keep {
|
||||
result.extend_from_slice(&lines[current_section_start..]);
|
||||
}
|
||||
|
||||
result.join("\n")
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn generate_muhe_prompt(stats: FlywheelStats) -> String {
|
||||
// ── 队列对比表 ────────────────────────────────────────────────────────────
|
||||
@ -1456,7 +1511,9 @@ pub fn generate_muhe_prompt(stats: FlywheelStats) -> String {
|
||||
|
||||
---
|
||||
|
||||
## 当前 CONVENTIONS.md(v{conventions_version})完整内容
|
||||
## 当前 CONVENTIONS.md(v{conventions_version})规则摘要
|
||||
|
||||
> 以下仅包含对「固化/遗忘」判断有价值的规则段落,格式定义和流程说明已省略。
|
||||
|
||||
{conventions}
|
||||
|
||||
@ -1500,8 +1557,8 @@ pub fn generate_muhe_prompt(stats: FlywheelStats) -> String {
|
||||
ai_stale_summary = ai_stale_summary,
|
||||
iteration_summary = iteration_summary,
|
||||
stalled_modules_summary = stalled_modules_summary,
|
||||
conventions_version = MASTER_CONVENTIONS_VERSION,
|
||||
conventions = MASTER_CONVENTIONS,
|
||||
conventions_version = master_conventions_version(),
|
||||
conventions = conventions_rules_summary(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1585,6 +1642,47 @@ fn append_usage_snapshot(bp_dir: &Path, manifest: &BlueprintManifest, stats: &Bl
|
||||
.map(|m| m.id.clone())
|
||||
.collect();
|
||||
|
||||
// 当前所有任务的状态快照(用于 delta 计算和下次快照对比)
|
||||
let mut current_done: std::collections::HashSet<String> = Default::default();
|
||||
let mut current_blocked: std::collections::HashSet<String> = Default::default();
|
||||
for module in &manifest.modules {
|
||||
for task in &module.tasks {
|
||||
let key = format!("{}/{}", module.id, task.title);
|
||||
match task.status.as_str() {
|
||||
"done" => { current_done.insert(key); }
|
||||
"blocked" => { current_blocked.insert(key); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 与上一条快照对比,生成 delta
|
||||
let (newly_done, newly_blocked, unblocked): (Vec<String>, Vec<String>, Vec<String>) = {
|
||||
let prev = root["snapshots"].as_array()
|
||||
.and_then(|arr| arr.iter().max_by_key(|s| s["at"].as_str().unwrap_or("")));
|
||||
|
||||
if let Some(prev_snap) = prev {
|
||||
let prev_done: std::collections::HashSet<String> = prev_snap["task_ids"]["done"]
|
||||
.as_array().map(|a| a.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())
|
||||
.unwrap_or_default();
|
||||
let prev_blocked: std::collections::HashSet<String> = prev_snap["task_ids"]["blocked"]
|
||||
.as_array().map(|a| a.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut nd: Vec<String> = current_done.difference(&prev_done).cloned().collect();
|
||||
let mut nb: Vec<String> = current_blocked.difference(&prev_blocked).cloned().collect();
|
||||
let mut ub: Vec<String> = prev_blocked.difference(¤t_blocked).cloned().collect();
|
||||
nd.sort(); nb.sort(); ub.sort();
|
||||
(nd, nb, ub)
|
||||
} else {
|
||||
(Vec::new(), Vec::new(), Vec::new())
|
||||
}
|
||||
};
|
||||
|
||||
let mut done_ids: Vec<String> = current_done.into_iter().collect();
|
||||
let mut blocked_ids: Vec<String> = current_blocked.into_iter().collect();
|
||||
done_ids.sort(); blocked_ids.sort();
|
||||
|
||||
let snapshot = serde_json::json!({
|
||||
"at": at,
|
||||
"conventions_version": conventions_version,
|
||||
@ -1596,7 +1694,7 @@ fn append_usage_snapshot(bp_dir: &Path, manifest: &BlueprintManifest, stats: &Bl
|
||||
"planned": stats.planned,
|
||||
"concept": stats.concept,
|
||||
"blocked": stats.blocked,
|
||||
"stalled": stalled_modules // in_progress 但无可推进任务的模块 id 列表
|
||||
"stalled": stalled_modules
|
||||
},
|
||||
"tasks": {
|
||||
"total": stats.total_tasks,
|
||||
@ -1606,7 +1704,18 @@ fn append_usage_snapshot(bp_dir: &Path, manifest: &BlueprintManifest, stats: &Bl
|
||||
"todo": tasks_todo,
|
||||
"dispatched": stats.dispatchable
|
||||
},
|
||||
"blocked_reasons": blocked_reasons
|
||||
"blocked_reasons": blocked_reasons,
|
||||
// 当前任务状态快照,供下次 delta 计算使用
|
||||
"task_ids": {
|
||||
"done": done_ids,
|
||||
"blocked": blocked_ids
|
||||
},
|
||||
// 与上一条快照的 delta(首次快照为空)
|
||||
"delta": {
|
||||
"newly_done": newly_done,
|
||||
"newly_blocked": newly_blocked,
|
||||
"unblocked": unblocked
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(arr) = root["snapshots"].as_array_mut() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user