agents: record visits eagerly to prevent concurrent collisions

Move visit recording from after LLM completion to immediately after
seed selection. With 15 concurrent agents, they all queried the same
graph state and selected the same high-degree seeds (core-personality
written 12x, irc-regulars 10x). Now the not-visited filter sees the
claim before concurrent agents query.

Narrows the race window from minutes (LLM call duration) to
milliseconds (store load to visit write). Full elimination would
require store refresh before query, but this handles the common case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kent Overstreet 2026-03-20 12:29:32 -04:00
parent 34e74ca2c5
commit 3fc108a251

View file

@ -73,6 +73,11 @@ pub fn run_one_agent_with_keys(
all_keys.extend(extra_keys);
let agent_batch = super::prompts::AgentBatch { prompt, node_keys: all_keys };
// Record visits eagerly so concurrent agents pick different seeds
if !agent_batch.node_keys.is_empty() {
store.record_agent_visits(&agent_batch.node_keys, agent_name).ok();
}
run_one_agent_inner(store, agent_name, &def, agent_batch, llm_tag, log, debug)
}
@ -90,6 +95,12 @@ pub fn run_one_agent(
log("building prompt");
let agent_batch = super::defs::run_agent(store, &def, batch_size)?;
// Eagerly record visits so concurrent agents pick different seeds.
// The not-visited query filter checks this timestamp.
if !agent_batch.node_keys.is_empty() {
store.record_agent_visits(&agent_batch.node_keys, agent_name).ok();
}
run_one_agent_inner(store, agent_name, &def, agent_batch, llm_tag, log, debug)
}
@ -132,11 +143,6 @@ fn run_one_agent_inner(
if debug { print!("{}", response_section); }
log(&format!("response {}KB", output.len() / 1024));
// Record visits for processed nodes
if !agent_batch.node_keys.is_empty() {
store.record_agent_visits(&agent_batch.node_keys, agent_name).ok();
}
Ok(AgentResult {
output,
node_keys: agent_batch.node_keys,