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 内容和版本(编译时嵌入)────────────────────
|
// ── 主节点:master CONVENTIONS.md 内容和版本(编译时嵌入)────────────────────
|
||||||
|
|
||||||
const MASTER_CONVENTIONS: &str = include_str!("../../../.blueprint/CONVENTIONS.md");
|
const MASTER_CONVENTIONS: &str = include_str!("../../../.blueprint/CONVENTIONS.md");
|
||||||
const MASTER_CONVENTIONS_VERSION: &str = "1.4.2";
|
|
||||||
|
|
||||||
// CLAUDE.md 蓝图管理区模板(版本与 CONVENTIONS 保持一致)
|
/// 从 MASTER_CONVENTIONS frontmatter 自动解析版本号,无需手动维护常量。
|
||||||
const CLAUDE_MANAGED_TEMPLATE: &str = r#"<!-- BLUEPRINT_MANAGED_START version="1.4.2" -->
|
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/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
|
本项目使用 `.blueprint/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
|
||||||
@ -30,7 +44,8 @@ const CLAUDE_MANAGED_TEMPLATE: &str = r#"<!-- BLUEPRINT_MANAGED_START version="1
|
|||||||
详见 `.blueprint/CONVENTIONS.md`,核心规则:
|
详见 `.blueprint/CONVENTIONS.md`,核心规则:
|
||||||
- 任务卡同时具备 `files` + `acceptance` + complexity `S/M` → 标记为 📋 可派发
|
- 任务卡同时具备 `files` + `acceptance` + complexity `S/M` → 标记为 📋 可派发
|
||||||
- 可派发的任务卡可以直接交给 Claude Agent 独立完成
|
- 可派发的任务卡可以直接交给 Claude Agent 独立完成
|
||||||
<!-- BLUEPRINT_MANAGED_END -->"#;
|
<!-- BLUEPRINT_MANAGED_END -->"#)
|
||||||
|
}
|
||||||
|
|
||||||
// ── manifest.yaml 对应结构 ───────────────────────────────────────────────────
|
// ── manifest.yaml 对应结构 ───────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -896,7 +911,7 @@ pub fn get_blueprint_status(project_path: String) -> Result<BlueprintStatus, Str
|
|||||||
claude_md_managed: false,
|
claude_md_managed: false,
|
||||||
claude_md_version: None,
|
claude_md_version: None,
|
||||||
manifest_updated: 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 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_managed
|
||||||
&& claude_md_version.as_deref() == Some(MASTER_CONVENTIONS_VERSION);
|
&& claude_md_version.as_deref() == Some(master_conventions_version());
|
||||||
|
|
||||||
let status = if !rules_current {
|
let status = if !rules_current {
|
||||||
"rules_outdated".to_string()
|
"rules_outdated".to_string()
|
||||||
@ -938,7 +953,7 @@ pub fn get_blueprint_status(project_path: String) -> Result<BlueprintStatus, Str
|
|||||||
claude_md_managed,
|
claude_md_managed,
|
||||||
claude_md_version,
|
claude_md_version,
|
||||||
manifest_updated,
|
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
|
// 2. 处理 CLAUDE.md
|
||||||
let claude_path = root.join("CLAUDE.md");
|
let claude_path = root.join("CLAUDE.md");
|
||||||
|
let managed_template = claude_managed_template();
|
||||||
let claude_action = if !claude_path.exists() {
|
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"));
|
root.file_name().and_then(|n| n.to_str()).unwrap_or("Project"));
|
||||||
std::fs::write(&claude_path, content)
|
std::fs::write(&claude_path, content)
|
||||||
.map_err(|e| format!("创建 CLAUDE.md 失败: {e}"))?;
|
.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") {
|
if content.contains("<!-- BLUEPRINT_MANAGED_START") {
|
||||||
// 替换现有 MANAGED 区
|
// 替换现有 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)
|
std::fs::write(&claude_path, new_content)
|
||||||
.map_err(|e| format!("更新 CLAUDE.md 失败: {e}"))?;
|
.map_err(|e| format!("更新 CLAUDE.md 失败: {e}"))?;
|
||||||
"updated".to_string()
|
"updated".to_string()
|
||||||
} else {
|
} else {
|
||||||
// 追加 MANAGED 区
|
// 追加 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)
|
std::fs::write(&claude_path, new_content)
|
||||||
.map_err(|e| format!("追加 CLAUDE.md 失败: {e}"))?;
|
.map_err(|e| format!("追加 CLAUDE.md 失败: {e}"))?;
|
||||||
"appended".to_string()
|
"appended".to_string()
|
||||||
@ -995,7 +1011,7 @@ pub fn sync_blueprint_rules(project_path: String) -> Result<SyncResult, String>
|
|||||||
success: true,
|
success: true,
|
||||||
conventions_written: true,
|
conventions_written: true,
|
||||||
claude_md_action: claude_action.clone(),
|
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]
|
#[tauri::command]
|
||||||
pub fn generate_muhe_prompt(stats: FlywheelStats) -> String {
|
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}
|
{conventions}
|
||||||
|
|
||||||
@ -1500,8 +1557,8 @@ pub fn generate_muhe_prompt(stats: FlywheelStats) -> String {
|
|||||||
ai_stale_summary = ai_stale_summary,
|
ai_stale_summary = ai_stale_summary,
|
||||||
iteration_summary = iteration_summary,
|
iteration_summary = iteration_summary,
|
||||||
stalled_modules_summary = stalled_modules_summary,
|
stalled_modules_summary = stalled_modules_summary,
|
||||||
conventions_version = MASTER_CONVENTIONS_VERSION,
|
conventions_version = master_conventions_version(),
|
||||||
conventions = MASTER_CONVENTIONS,
|
conventions = conventions_rules_summary(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1585,6 +1642,47 @@ fn append_usage_snapshot(bp_dir: &Path, manifest: &BlueprintManifest, stats: &Bl
|
|||||||
.map(|m| m.id.clone())
|
.map(|m| m.id.clone())
|
||||||
.collect();
|
.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!({
|
let snapshot = serde_json::json!({
|
||||||
"at": at,
|
"at": at,
|
||||||
"conventions_version": conventions_version,
|
"conventions_version": conventions_version,
|
||||||
@ -1596,7 +1694,7 @@ fn append_usage_snapshot(bp_dir: &Path, manifest: &BlueprintManifest, stats: &Bl
|
|||||||
"planned": stats.planned,
|
"planned": stats.planned,
|
||||||
"concept": stats.concept,
|
"concept": stats.concept,
|
||||||
"blocked": stats.blocked,
|
"blocked": stats.blocked,
|
||||||
"stalled": stalled_modules // in_progress 但无可推进任务的模块 id 列表
|
"stalled": stalled_modules
|
||||||
},
|
},
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"total": stats.total_tasks,
|
"total": stats.total_tasks,
|
||||||
@ -1606,7 +1704,18 @@ fn append_usage_snapshot(bp_dir: &Path, manifest: &BlueprintManifest, stats: &Bl
|
|||||||
"todo": tasks_todo,
|
"todo": tasks_todo,
|
||||||
"dispatched": stats.dispatchable
|
"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() {
|
if let Some(arr) = root["snapshots"].as_array_mut() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user