feat(self-audit): continuous_improvement audits the project record it can actually reach
Its first run could not do its job. The template audited "every project
agent's .brain" through per-agent APIs — fetch a brain, submit to
/api/claws/{id}/level-up, pull claw metrics — none of which a mission can
reach; agent brains live in the server's /data/brains volume and nothing
delivered them in. It spent itself searching, found a ROSTER.md in a
scratch repo, and audited that.
A delivery channel alone would not have helped: per-mission crews carry
~2 KB seed brains with no history, because missions write memory to the
REPOSITORY brain, one judge verdict per phase. That is where a project's
history actually accumulates, so that is the subject now.
mission_memory::export renders the whole repo brain as markdown — the
.brain is HDF5 and a mission container has no library to read it — and
mission_orchestrator installs it at /mission/memory/PROJECT-MEMORY.md,
outside the checkout so it is input and never lands in the diff, the same
way install_skill_files delivers skills.
The three roles are rewritten for that record: an inspector that finds
patterns (several UNMET lines on the same kind of work) and quotes them;
a proposer that ties each proposal to at least two lines or drops it; and
an evaluator that checks the cited lines exist verbatim and marks each
proposal SUPPORTED, WEAK or UNSUPPORTED. Each says outright that "no
change is warranted" is a complete result — the property that kept the
first run from inventing improvements out of empty brains.
Local: 523 passed; the two DB-backed world tests panic PoolTimedOut
because Docker Desktop is down here. CI runs them against real Postgres.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a260499060
commit
642b6ef44b
@@ -385,6 +385,16 @@ pub async fn on_launch(
|
||||
crate::skill_delivery::resolve(requested, installed),
|
||||
)
|
||||
.await;
|
||||
|
||||
// The repository's whole memory, readable, beside the skills. The
|
||||
// brief already carries the three most relevant verdicts; this is
|
||||
// the full record, for work whose subject IS the record — see
|
||||
// `mission_memory::export` for why it was needed.
|
||||
if mission_gateway.is_some() {
|
||||
if let Some(repo) = mission.repo_id {
|
||||
install_project_memory(repo, mission_id, &container).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut first_team_id: Option<Uuid> = None;
|
||||
let mut provisioned_claws: Vec<cm_domain::AgentId> = Vec::new();
|
||||
@@ -1085,6 +1095,52 @@ async fn install_skill_files(
|
||||
true
|
||||
}
|
||||
|
||||
/// Write the repository's memory export into the mission container.
|
||||
///
|
||||
/// Best-effort and loud: a mission with no memory to read is an ordinary
|
||||
/// mission, and a first mission on a repository has none. Outside the
|
||||
/// checkout (`mission_memory::MEMORY_DIR`) so it never lands in the diff.
|
||||
async fn install_project_memory(repo_id: Uuid, mission_id: Uuid, container: &str) {
|
||||
let Some(md) = crate::mission_memory::export(repo_id) else {
|
||||
return;
|
||||
};
|
||||
let docker = match crate::container_exec::connect() {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
eprintln!("mission_orchestrator: cannot reach docker for project memory: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dir = crate::mission_memory::MEMORY_DIR;
|
||||
let argv = vec!["sh".to_string(), "-lc".to_string(), format!("mkdir -p {dir}")];
|
||||
if !matches!(
|
||||
crate::container_exec::exec_as_root(
|
||||
&docker,
|
||||
container,
|
||||
None,
|
||||
&argv,
|
||||
crate::container_tool_hooks::INSTALL_TIMEOUT,
|
||||
)
|
||||
.await,
|
||||
Ok(out) if out.exit_code == Some(0)
|
||||
) {
|
||||
eprintln!("mission_orchestrator: could not create {dir} for mission {mission_id}");
|
||||
return;
|
||||
}
|
||||
let bytes = md.len();
|
||||
let files = vec![(crate::mission_memory::MEMORY_FILE.to_string(), md.into_bytes())];
|
||||
match crate::mission_fs::put_files(&docker, container, dir, &files).await {
|
||||
Ok(()) => eprintln!(
|
||||
"mission_orchestrator: project memory ({bytes} bytes) installed for mission \
|
||||
{mission_id} at {dir}/{}",
|
||||
crate::mission_memory::MEMORY_FILE
|
||||
),
|
||||
Err(e) => eprintln!(
|
||||
"mission_orchestrator: could not write project memory for {mission_id}: {e}"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Record which arm this mission runs, so every turn composes the same one and
|
||||
/// the score can be attributed to it afterwards.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user