agents: deduplicate timestamps, plan expansion, rename agent

- Add compact_timestamp() to store — replaces 5 copies of
  format_datetime(now_epoch()).replace([':', '-', 'T'], "")
  Also fixes missing seconds (format_datetime only had HH:MM).

- Add ConsolidationPlan::to_agent_runs() — replaces identical
  plan-to-runs-list expansion in consolidate.rs and daemon.rs.

- Port job_rename_agent to use run_one_agent — eliminates manual
  prompt building, LLM call, report storage, and visit recording
  that duplicated the shared pipeline.

- Rename Confidence::weight()/value() to delta_weight()/gate_value()
  to clarify the distinction (delta metrics vs depth gating).
This commit is contained in:
ProofOfConcept 2026-03-10 17:48:00 -04:00
parent fe7f636ad3
commit abab85d249
6 changed files with 49 additions and 82 deletions

View file

@ -133,8 +133,7 @@ fn job_consolidation_agent(
ctx.log_line(&format!("running agent: {} (batch={})", agent, batch));
let result = super::knowledge::run_one_agent(&mut store, &agent, batch, "consolidate")?;
let ts = crate::store::format_datetime(crate::store::now_epoch())
.replace([':', '-', 'T'], "");
let ts = crate::store::compact_timestamp();
let mut applied = 0;
for action in &result.actions {
if super::knowledge::apply_action(&mut store, action, &agent, &ts, 0) {
@ -157,34 +156,24 @@ fn job_rename_agent(
let mut store = crate::store::Store::load()?;
let batch = if batch_size == 0 { 10 } else { batch_size };
ctx.log_line(&format!("building prompt: rename (batch={})", batch));
ctx.log_line(&format!("running rename agent (batch={})", batch));
let agent_batch = super::prompts::agent_prompt(&store, "rename", batch)?;
ctx.log_line(&format!("prompt: {} chars ({} nodes), calling Sonnet",
agent_batch.prompt.len(), agent_batch.node_keys.len()));
let result = super::knowledge::run_one_agent(&mut store, "rename", batch, "consolidate")?;
let response = super::llm::call_sonnet("consolidate", &agent_batch.prompt)?;
// Parse RENAME actions directly from response
// Parse RENAME actions from response (rename uses its own format, not WRITE_NODE/LINK/REFINE)
let mut applied = 0;
let mut skipped = 0;
let mut successfully_renamed: Vec<String> = Vec::new();
for line in response.lines() {
for line in result.output.lines() {
let trimmed = line.trim();
if !trimmed.starts_with("RENAME ") { continue; }
let rest = &trimmed[7..];
// Split on first space after the old key — tricky because keys contain spaces? No, they don't.
// Keys are single tokens with hyphens/underscores/hashes.
let parts: Vec<&str> = rest.splitn(2, ' ').collect();
let parts: Vec<&str> = trimmed[7..].splitn(2, ' ').collect();
if parts.len() != 2 { skipped += 1; continue; }
let old_key = parts[0].trim();
let new_key = parts[1].trim();
if old_key.is_empty() || new_key.is_empty() { skipped += 1; continue; }
// Resolve old key (handles partial matches)
let resolved = match store.resolve_key(old_key) {
Ok(k) => k,
Err(e) => {
@ -194,7 +183,6 @@ fn job_rename_agent(
}
};
// Don't rename to something that already exists
if store.nodes.contains_key(new_key) {
ctx.log_line(&format!("skip: {} already exists", new_key));
skipped += 1;
@ -204,7 +192,6 @@ fn job_rename_agent(
match store.rename_node(&resolved, new_key) {
Ok(()) => {
ctx.log_line(&format!("renamed: {}{}", resolved, new_key));
successfully_renamed.push(new_key.to_string());
applied += 1;
}
Err(e) => {
@ -218,20 +205,6 @@ fn job_rename_agent(
store.save()?;
}
// Record visits for successfully renamed nodes
if !successfully_renamed.is_empty() {
if let Err(e) = store.record_agent_visits(&successfully_renamed, "rename") {
ctx.log_line(&format!("visit recording: {}", e));
}
}
// Also store the report for auditing
let ts = crate::store::format_datetime(crate::store::now_epoch())
.replace([':', '-', 'T'], "");
let report_key = format!("_consolidation-rename-{}", ts);
store.upsert_provenance(&report_key, &response,
crate::store::Provenance::AgentConsolidate).ok();
ctx.log_line(&format!("done: {} applied, {} skipped", applied, skipped));
Ok(())
})
@ -1107,25 +1080,7 @@ pub fn run_daemon() -> Result<(), String> {
};
let batch_size = 5;
// Build the list of (agent_type, batch_size) runs
let mut runs: Vec<(&str, usize)> = Vec::new();
if plan.run_health {
runs.push(("health", 0));
}
for (name, count) in [
("replay", plan.replay_count),
("linker", plan.linker_count),
("separator", plan.separator_count),
("transfer", plan.transfer_count),
] {
let mut remaining = count;
while remaining > 0 {
let batch = remaining.min(batch_size);
runs.push((name, batch));
remaining -= batch;
}
}
let runs = plan.to_agent_runs(batch_size);
log_event("scheduler", "consolidation-plan",
&format!("{} agents ({}r {}l {}s {}t)",