fix(growth,kernel,runtime): 穷尽审计后 7 项修复 — body 持久化 + embedding 死路径 + 安全加固
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

CRITICAL 修复:
- body_markdown 数据丢失: SkillManifest.body 字段 + serialize_skill_md 使用 body 替代默认内容
- embedding 检索死路径: rerank_entries 使用异步 index_entry_with_embedding + score_similarity_with_embedding (70/30 混合)
- try_write 静默丢失: pending_embedding 字段 + apply_pending_embedding() 延迟应用

IMPORTANT 修复:
- auto_mode 内存泄漏: add_pending 容量限制 100 + 溢出时丢弃最旧
- name_to_slug 空 ID: uuid fallback for empty/whitespace-only names
- compaction embedding 缺失: compaction GrowthIntegration 也接收 embedding
- kernel 未初始化警告: viking_configure_embedding warn log

验证: 934+ tests PASS, 0 failures
This commit is contained in:
iven
2026-04-21 17:27:37 +08:00
parent 5b5491a08f
commit a43806ccc2
13 changed files with 97 additions and 20 deletions

View File

@@ -13,7 +13,7 @@ use zclaw_types::SkillId;
/// Safety invariants:
/// - `mode` is always `PromptOnly` (auto-generated skills cannot execute code)
/// - `enabled` is `false` (requires one explicit positive feedback to activate)
/// - `body_markdown` becomes the SKILL.md body content (stored by serialize_skill_md)
/// - `body_markdown` is stored in `manifest.body` and persisted by `serialize_skill_md`
pub fn candidate_to_manifest(candidate: &SkillCandidate) -> SkillManifest {
let slug = name_to_slug(&candidate.name);
@@ -32,6 +32,7 @@ pub fn candidate_to_manifest(candidate: &SkillCandidate) -> SkillManifest {
triggers: candidate.triggers.clone(),
tools: candidate.tools.clone(),
enabled: false,
body: Some(candidate.body_markdown.clone()),
}
}
@@ -48,7 +49,13 @@ fn name_to_slug(name: &str) -> String {
result.push_str(&format!("{:x}", c as u32));
}
}
result.trim_matches('-').to_string()
let slug = result.trim_matches('-').to_string();
if slug.is_empty() {
// Fallback for empty or whitespace-only names
format!("skill-{}", &uuid::Uuid::new_v4().to_string()[..8])
} else {
slug
}
}
#[cfg(test)]

View File

@@ -324,6 +324,9 @@ impl Kernel {
if let Some(ref driver) = self.extraction_driver {
growth_for_compaction = growth_for_compaction.with_llm_driver(driver.clone());
}
if let Some(ref embed_client) = self.embedding_client {
growth_for_compaction.configure_embedding(embed_client.clone());
}
let mw = zclaw_runtime::middleware::compaction::CompactionMiddleware::new(
threshold,
zclaw_runtime::CompactionConfig::default(),