Audit→CAS, reload rules, time-decay y forma canónica del hash chain

- AuditLog::flush_to_cas() persiste entries pendientes con bytes canónicos
  (sha=[0;32]) para que CAS-sha == entry.sha. AuditHeadPointer en disco
  tras cada flush — verificación posterior sin escanear el log entero.
- IntrospectRequest::FlushAudit / ReloadRules. brainctl flush-audit / reload.
- Auto-flush task cada 10s cuando --audit-head está configurado.
- ReloadRules { path? } vacía engine + carga (.k vía kcl CLI o .json).
- Observer con time-decay opcional: count * 0.5^(age/half_life).
  conditional_prob y pmi consumen valores decayed transparentemente.
- --brain-half-life flag CLI.
- KCL Rust SDK descartado: kcl-* en crates.io son del proyecto KittyCAD,
  no KusionStack. Subprocess al CLI sigue siendo la vía canónica.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-03 23:16:41 +00:00
parent d6b8f18b43
commit a4fa42c781
6 changed files with 282 additions and 40 deletions
+19 -1
View File
@@ -51,9 +51,14 @@ async fn main() -> anyhow::Result<()> {
let limit: usize = args.get(2).and_then(|s| s.parse().ok()).unwrap_or(20);
IntrospectRequest::ListAudit { limit }
}
"flush-audit" => IntrospectRequest::FlushAudit,
"reload" => {
let path = args.get(2).cloned();
IntrospectRequest::ReloadRules { path }
}
other => {
eprintln!("subcomando desconocido: {other}");
eprintln!("válidos: list-rules | entropy | top <n> | crystals | crystal-kcl <i> | promote <i> | remove <ulid> | audit <limit>");
eprintln!("válidos: list-rules | entropy | top <n> | crystals | crystal-kcl <i> | promote <i> | remove <ulid> | audit <limit> | flush-audit | reload [path]");
std::process::exit(2);
}
};
@@ -116,6 +121,15 @@ fn print_response(r: &IntrospectResponse) {
e.seq, e.timestamp_ms, prev, sha, e.action);
}
}
IntrospectResponse::Flushed { written, head_sha, total_flushed } => {
println!("flushed: {written} entries esta pasada, total acumulado: {total_flushed}");
if let Some(sha) = head_sha {
println!("head sha: {}", hex_long(*sha));
}
}
IntrospectResponse::Reloaded { count } => {
println!("reload OK: {count} reglas activas tras reload");
}
IntrospectResponse::Error(e) => eprintln!("error: {e}"),
}
}
@@ -123,3 +137,7 @@ fn print_response(r: &IntrospectResponse) {
fn hex_short(sha: [u8; 32]) -> String {
sha[..4].iter().map(|b| format!("{:02x}", b)).collect::<String>() + ".."
}
fn hex_long(sha: [u8; 32]) -> String {
sha.iter().map(|b| format!("{:02x}", b)).collect()
}