slice 3.5d: agent_template_link + brain seed helper + skills merge
Ships the lineage layer that ties agents back to their team template
and wires the MCP skills server to actually merge template default
skills with per-agent overrides.
Migration 0050 adds `agent_template_link` (agent_id PK, template_id,
template_version, role_slot, seeded_at, created_at + indexes for
template/role lookups). Populated at agent-materialization time by
Slice 4's mission-launch orchestrator; read here by the skills MCP
server and by future level-up (Slice 8.5).
New Rust surface:
- cm_db::repo::agent_template_link (upsert / get / mark_seeded /
agents_for_template — the last is what level-up's "prompt upgrade
on template N+1" query needs)
- cm_api::brain_seed::ingest(claw_id, seed_md, identity_prompt)
opens cm_brain::ClawBrain on spawn_blocking, sets system_prompt
on first touch, writes seed as agent_md, commits. Idempotent —
skips when agent_md already populated.
- cm_api::mcp_skills::mcp_skills tools/call now resolves the caller
agent's template + role via agent_template_link and merges
template default skills with per-agent overrides (was overrides-
only in Slice 3.5b).
- cm_api::team_template_loader now binds template_role_skills after
upserting each template — looks up each declared skill by name,
attaches with pin_in_context=true for foundation skills and the
first two role skills. Missing skills log + skip.
- Boot ordering: skills load BEFORE team templates so the binding
lookup resolves.
Follow-up (Slice 4): mission-launch orchestrator calls brain_seed::ingest
+ agent_template_link::upsert when minting a team from a template.
Until that lands, the link is populated only by manual writes; the
MCP merge is silent-no-op for agents without a link (falls through
to overrides-only), which matches the pre-3.5d behavior.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
520a717dec
commit
85a97dffca
@@ -0,0 +1,115 @@
|
||||
//! Agent → team-template lineage. Slice 3.5d of the missions
|
||||
//! consolidation. Populated by the team materialization path when a
|
||||
//! new agent is created from a template role; read by:
|
||||
//! - the `clawmates_skills` MCP server to resolve the caller's
|
||||
//! template + role_slot so it can merge template default skills
|
||||
//! with agent overrides
|
||||
//! - level-up (Slice 8.5) to diff learned-vs-seeded knowledge and
|
||||
//! route "promote to template" proposals
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
use sqlx::Row;
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::DbError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentTemplateLink {
|
||||
pub agent_id: Uuid,
|
||||
pub template_id: Uuid,
|
||||
pub template_version: i32,
|
||||
pub role_slot: String,
|
||||
#[serde(with = "time::serde::rfc3339::option")]
|
||||
pub seeded_at: Option<OffsetDateTime>,
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub created_at: OffsetDateTime,
|
||||
}
|
||||
|
||||
/// Idempotent link insert. If the agent already has a link (rare —
|
||||
/// only via manual overrides) we UPDATE the version/role rather than
|
||||
/// error, since the intended template may have shifted.
|
||||
pub async fn upsert(
|
||||
pool: &PgPool,
|
||||
agent_id: Uuid,
|
||||
template_id: Uuid,
|
||||
template_version: i32,
|
||||
role_slot: &str,
|
||||
) -> Result<(), DbError> {
|
||||
sqlx::query(
|
||||
"INSERT INTO agent_template_link
|
||||
(agent_id, template_id, template_version, role_slot)
|
||||
VALUES ($1,$2,$3,$4)
|
||||
ON CONFLICT (agent_id) DO UPDATE SET
|
||||
template_id = EXCLUDED.template_id,
|
||||
template_version = EXCLUDED.template_version,
|
||||
role_slot = EXCLUDED.role_slot",
|
||||
)
|
||||
.bind(agent_id)
|
||||
.bind(template_id)
|
||||
.bind(template_version)
|
||||
.bind(role_slot)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn mark_seeded(pool: &PgPool, agent_id: Uuid) -> Result<(), DbError> {
|
||||
sqlx::query(
|
||||
"UPDATE agent_template_link SET seeded_at = now()
|
||||
WHERE agent_id = $1 AND seeded_at IS NULL",
|
||||
)
|
||||
.bind(agent_id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get(pool: &PgPool, agent_id: Uuid) -> Result<Option<AgentTemplateLink>, DbError> {
|
||||
let row = sqlx::query(
|
||||
"SELECT agent_id, template_id, template_version, role_slot,
|
||||
seeded_at, created_at
|
||||
FROM agent_template_link WHERE agent_id = $1",
|
||||
)
|
||||
.bind(agent_id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
Ok(row.map(|r| AgentTemplateLink {
|
||||
agent_id: r.get("agent_id"),
|
||||
template_id: r.get("template_id"),
|
||||
template_version: r.get("template_version"),
|
||||
role_slot: r.get("role_slot"),
|
||||
seeded_at: r.get("seeded_at"),
|
||||
created_at: r.get("created_at"),
|
||||
}))
|
||||
}
|
||||
|
||||
/// All agents provisioned from a given template — used by level-up
|
||||
/// when a template gains a new version and we want to prompt each
|
||||
/// downstream agent for upgrade.
|
||||
pub async fn agents_for_template(
|
||||
pool: &PgPool,
|
||||
template_id: Uuid,
|
||||
) -> Result<Vec<AgentTemplateLink>, DbError> {
|
||||
let rows = sqlx::query(
|
||||
"SELECT agent_id, template_id, template_version, role_slot,
|
||||
seeded_at, created_at
|
||||
FROM agent_template_link WHERE template_id = $1
|
||||
ORDER BY created_at",
|
||||
)
|
||||
.bind(template_id)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.map(|r| AgentTemplateLink {
|
||||
agent_id: r.get("agent_id"),
|
||||
template_id: r.get("template_id"),
|
||||
template_version: r.get("template_version"),
|
||||
role_slot: r.get("role_slot"),
|
||||
seeded_at: r.get("seeded_at"),
|
||||
created_at: r.get("created_at"),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod agent_containers;
|
||||
pub mod agent_template_link;
|
||||
pub mod agents;
|
||||
pub mod audit;
|
||||
pub mod cleanup;
|
||||
|
||||
Reference in New Issue
Block a user