Compare commits

..

2 Commits

Author SHA1 Message Date
lanrtop
7cfbfb1b86 chore(blueprint): onboarding-pack 模块收官——U1/U3/B4 实调终验通过
All checks were successful
Push & PR Check / check (push) Successful in 51s
批量分发 25 项目 0 失败(envoy/gate 全托管升级生效)、ingest 清洗
链路贯通、verify 七项链准确检出 lefthook 缺失键。模块 status→done。
2026-07-15 19:53:49 +09:00
lanrtop
473cf7e92b fix(onboarding/dispatch): 空路径登记防护——拒绝分发而非写进进程 cwd
批量实调实锤:两条空路径登记(银龙集团应用/agent编排界面)使
Path::new("") 变相对路径,接入包整套写进了 dev 进程的 src-tauri/。
apply_onboarding_pack 入口拦截空/不存在路径(单点防护),批量 SQL
同步过滤空串,回归测试驻军。

Rules-Applied: R06,R07
2026-07-15 19:53:47 +09:00
4 changed files with 22 additions and 4 deletions

View File

@ -370,8 +370,8 @@ modules:
- id: onboarding-pack - id: onboarding-pack
name: 接入包 name: 接入包
area: backend area: backend
status: in_progress status: done
progress: 99 progress: 100
note: "体系分发主线全卡完成:…/V1/U3/B4/U1 ✅2026-07-15 择升环三断点修复收官U3 规则校验 / B4 沉默项目信号 / U1 全托管升级+批量重分发。U1 批量模式 MCP 实调待重建安装后终验,通过后模块收官 done" note: "体系分发主线全卡完成:…/V1/U3/B4/U1 ✅2026-07-15 择升环三断点修复收官U3 规则校验 / B4 沉默项目信号 / U1 全托管升级+批量重分发。U1 批量模式 MCP 实调待重建安装后终验,通过后模块收官 done"
position: [3500, 0] position: [3500, 0]
updated: 2026-07-15 updated: 2026-07-15

View File

@ -136,7 +136,7 @@
### ✅ U1. 全托管文件升级通道 + 批量重分发(择升环断点修复 ①) ### ✅ U1. 全托管文件升级通道 + 批量重分发(择升环断点修复 ①)
- status: done - status: done
- done_note: 2026-07-15 完成(分支 feat/onboarding-pack-u1 → PR。dispatch_fully_managed 三态written/skipped/updatedCRLF 归一化防覆盖抖动envoy.md/blueprint-gate.cjs 切换全托管并加「由炼境托管」声明头verify_onboarding 新增第 7 项「lefthook 检查段齐全」——期望键集合从内嵌 TMPL_LEFTHOOK 自省提取lefthook_command_keys 纯函数注释行不算键零硬编码MCP apply_onboarding_pack 支持批量模式project_id 字段缺失→全部已登记项目空串仍按无效单项目报错schema required 放宽为空并同步测试断言契约放宽属验收要求已声明。cargo test 102 全过。**遗留待终验**:批量模式 MCP 实调需重建安装炼境后执行(模板在二进制资源里,同 V1 先例batch_apply.py/scan_onboarded.py 暂保留(用户保留意见),实调终验通过后再清理 - done_note: 2026-07-15 完成(分支 feat/onboarding-pack-u1 → PR。dispatch_fully_managed 三态written/skipped/updatedCRLF 归一化防覆盖抖动envoy.md/blueprint-gate.cjs 切换全托管并加「由炼境托管」声明头verify_onboarding 新增第 7 项「lefthook 检查段齐全」——期望键集合从内嵌 TMPL_LEFTHOOK 自省提取lefthook_command_keys 纯函数注释行不算键零硬编码MCP apply_onboarding_pack 支持批量模式project_id 字段缺失→全部已登记项目空串仍按无效单项目报错schema required 放宽为空并同步测试断言契约放宽属验收要求已声明。cargo test 102 全过。**实调终验2026-07-15 dev 实例,遗留已销)**:批量分发 25 项目 0 失败——存量项目 envoy/gate 全部 updated全托管通道生效、AGENTS 块多项目原位升 v1.6.0ingest 全量重跑 19 项目清洗 0 条存量本就干净防线在位verify 抽查 enterprise-system 与炼境自身均 6/7唯一 fail 恰是新增「lefthook 检查段」并准确列出各自缺失键检测落后功能真实工作。PR #4 已 merge。batch_apply.py/scan_onboarded.py 可清理(用户处置)
- complexity: M - complexity: M
- depends: onboarding-pack卡 T1 - depends: onboarding-pack卡 T1
- files: src-tauri/src/commands/onboarding.rs, src-tauri/src/mcp_tools.rs, src-tauri/resources/onboarding/.claude/commands/envoy.md, src-tauri/resources/onboarding/.claude/hooks/blueprint-gate.cjs - files: src-tauri/src/commands/onboarding.rs, src-tauri/src/mcp_tools.rs, src-tauri/resources/onboarding/.claude/commands/envoy.md, src-tauri/resources/onboarding/.claude/hooks/blueprint-gate.cjs

View File

@ -264,6 +264,12 @@ pub fn apply_onboarding_pack(
project_path: String, project_path: String,
project_id: String, project_id: String,
) -> Result<OnboardingReport, String> { ) -> Result<OnboardingReport, String> {
// 空/无效路径必须在此拦截Path::new("") 是相对路径,模板会写进炼境进程的
// cwd2026-07-15 批量实调实锤:空路径登记把接入包写进了 src-tauri/
let root = Path::new(project_path.trim_end_matches(['/', '\\']));
if project_path.trim().is_empty() || !root.is_dir() {
return Err(format!("项目路径为空或不存在,拒绝分发: {project_path:?}"));
}
let templates = dispatch_templates(&project_path); let templates = dispatch_templates(&project_path);
let hook = run_lefthook_install(Path::new(project_path.trim_end_matches(['/', '\\']))); let hook = run_lefthook_install(Path::new(project_path.trim_end_matches(['/', '\\'])));
let buff = crate::commands::buff::apply_blueprint_buff(project_path, project_id)?; let buff = crate::commands::buff::apply_blueprint_buff(project_path, project_id)?;
@ -728,6 +734,18 @@ mod tests {
assert!(!check_managed_version(Some("手写内容,无标记"), expected).0, "无标记应 fail"); assert!(!check_managed_version(Some("手写内容,无标记"), expected).0, "无标记应 fail");
} }
/// 回归:空路径登记曾把接入包写进炼境进程 cwdsrc-tauri/2026-07-15 批量实调实锤)。
#[test]
fn apply_rejects_empty_or_missing_path() {
let err = apply_onboarding_pack(String::new(), "p".into()).unwrap_err();
assert!(err.contains("拒绝分发"), "空路径应拒绝: {err}");
let err = apply_onboarding_pack(" ".into(), "p".into()).unwrap_err();
assert!(err.contains("拒绝分发"), "空白路径应拒绝: {err}");
let err =
apply_onboarding_pack("E:/不存在的目录/xyz-404".into(), "p".into()).unwrap_err();
assert!(err.contains("拒绝分发"), "不存在路径应拒绝: {err}");
}
// ── U1全托管文件三态 + lefthook 检查段自省 ───────────────────────────── // ── U1全托管文件三态 + lefthook 检查段自省 ─────────────────────────────
#[test] #[test]

View File

@ -613,7 +613,7 @@ pub async fn tools_call(params: Option<&Value>) -> Value {
let conn = db::pool().get().map_err(|e| e.to_string())?; let conn = db::pool().get().map_err(|e| e.to_string())?;
let targets: Vec<(String, String)> = { let targets: Vec<(String, String)> = {
let mut stmt = conn let mut stmt = conn
.prepare("SELECT id, win_path FROM project_workspaces WHERE win_path IS NOT NULL") .prepare("SELECT id, win_path FROM project_workspaces WHERE win_path IS NOT NULL AND TRIM(win_path) != ''")
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let rows = stmt let rows = stmt
.query_map([], |r| Ok((r.get(0)?, r.get(1)?))) .query_map([], |r| Ok((r.get(0)?, r.get(1)?)))