mirror of
https://github.com/block/buzz.git
synced 2026-11-14 05:12:27 +00:00
fix(agents): propagate shared instruction edits
Refresh linked agent assignments from their definition at snapshot apply time and include the effective coordinates in restart drift detection. Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
@@ -433,6 +433,9 @@ pub struct PersonaSnapshot {
|
||||
pub system_prompt: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub provider: Option<String>,
|
||||
/// Exact publisher-pinned shared-instruction coordinates, copied verbatim
|
||||
/// from the definition so edits reach an existing instance on restart.
|
||||
pub assigned_relay_skills: Vec<String>,
|
||||
/// Preferred ACP runtime ID, copied verbatim from the persona (including
|
||||
/// `None`). Unlike `model`/`provider`, there is no record-fallback: the
|
||||
/// materialized instance `runtime` must mirror the definition so that
|
||||
@@ -454,14 +457,15 @@ pub fn persona_snapshot(persona: &AgentDefinition) -> PersonaSnapshot {
|
||||
system_prompt: Some(persona.system_prompt.clone()),
|
||||
model: persona.model.clone(),
|
||||
provider: persona.provider.clone(),
|
||||
assigned_relay_skills: persona.assigned_relay_skills.clone(),
|
||||
runtime: persona.runtime.clone(),
|
||||
source_version: persona_content_hash(&persona_event_content(persona)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-pin `record` to `persona`: build a snapshot via [`persona_snapshot`]
|
||||
/// and mirror it onto the record — the definition quad
|
||||
/// (`system_prompt`/`model`/`provider`/`runtime`), the env-override
|
||||
/// and mirror it onto the record — the definition fields
|
||||
/// (`system_prompt`/`model`/`provider`/`runtime`/shared instructions), the env-override
|
||||
/// self-heal, and the `persona_source_version` drift basis.
|
||||
///
|
||||
/// Definition-authoritative: blank definition model/provider produce `None`
|
||||
@@ -484,6 +488,7 @@ pub fn apply_persona_snapshot(record: &mut ManagedAgentRecord, persona: &AgentDe
|
||||
}
|
||||
record.model = snapshot.model;
|
||||
record.provider = snapshot.provider;
|
||||
record.assigned_relay_skills = snapshot.assigned_relay_skills;
|
||||
record.runtime = snapshot.runtime;
|
||||
// Drop a stale create-time harness pin when the definition switches to a
|
||||
// different known runtime (builtin, static preset, or loaded custom). A pin
|
||||
|
||||
@@ -168,6 +168,18 @@ pub(super) fn sample_persona() -> AgentDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_snapshot_refreshes_shared_instructions_for_existing_instance() {
|
||||
let mut record = sample_record();
|
||||
record.assigned_relay_skills = vec![format!("30023:{}:old", "a".repeat(64))];
|
||||
let mut persona = sample_persona();
|
||||
persona.assigned_relay_skills = vec![format!("30023:{}:new", "b".repeat(64))];
|
||||
|
||||
apply_persona_snapshot(&mut record, &persona);
|
||||
|
||||
assert_eq!(record.assigned_relay_skills, persona.assigned_relay_skills);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monotonic_created_at_bumps_past_head() {
|
||||
// No head: uses now (floor 0).
|
||||
|
||||
@@ -111,6 +111,8 @@ pub(crate) struct SpawnConfigSnapshot {
|
||||
pub system_prompt: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub provider: Option<String>,
|
||||
/// Exact publisher-pinned shared-instruction coordinates exported to ACP.
|
||||
pub assigned_relay_skills: Vec<String>,
|
||||
/// `None` when a user env override shadows `BUZZ_ACP_SESSION_TITLE`: spawn
|
||||
/// writes the title BEFORE the user env layer, so the override is what
|
||||
/// actually runs and it already reaches this snapshot through `env`.
|
||||
@@ -193,6 +195,7 @@ impl SpawnConfigSnapshot {
|
||||
system_prompt: system_prompt.map(str::to_string),
|
||||
model: model.map(str::to_string),
|
||||
provider: provider.map(str::to_string),
|
||||
assigned_relay_skills: record.assigned_relay_skills.clone(),
|
||||
session_title: (!descriptor.env.contains_key(SESSION_TITLE_ENV_VAR))
|
||||
.then(|| resolve_session_title(record.display_name.as_deref(), &record.name))
|
||||
.flatten(),
|
||||
|
||||
@@ -21,6 +21,7 @@ fn base() -> SpawnConfigSnapshot {
|
||||
system_prompt: Some("You are a test agent.".into()),
|
||||
model: Some("gpt-5".into()),
|
||||
provider: Some("openai".into()),
|
||||
assigned_relay_skills: vec![format!("30023:{}:review", "a".repeat(64))],
|
||||
session_title: Some("Fizz".into()),
|
||||
auth_tag: Some("tag-abcdefgh".into()),
|
||||
respond_to: "owner-only".into(),
|
||||
@@ -62,6 +63,7 @@ fn mutations() -> Vec<Mutation> {
|
||||
("system_prompt", |s| s.system_prompt = None),
|
||||
("model", |s| s.model = None),
|
||||
("provider", |s| s.provider = None),
|
||||
("assigned_relay_skills", |s| s.assigned_relay_skills.clear()),
|
||||
("session_title", |s| s.session_title = None),
|
||||
("auth_tag", |s| s.auth_tag = None),
|
||||
("respond_to", |s| s.respond_to = "anyone".into()),
|
||||
|
||||
@@ -223,6 +223,27 @@ fn persona_prompt_edit_changes_snapshot() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn persona_shared_instruction_edit_changes_snapshot() {
|
||||
let mut rec = record();
|
||||
rec.persona_id = Some("pers".into());
|
||||
let mut before = persona("pers", Some("goose"), "prompt");
|
||||
before.assigned_relay_skills = vec![format!("30023:{}:before", "a".repeat(64))];
|
||||
let mut after = before.clone();
|
||||
after.assigned_relay_skills = vec![format!("30023:{}:after", "b".repeat(64))];
|
||||
|
||||
assert_ne!(
|
||||
snapshot(
|
||||
&rec,
|
||||
&[before],
|
||||
&[],
|
||||
"wss://ws.example",
|
||||
&Default::default()
|
||||
),
|
||||
snapshot(&rec, &[after], &[], "wss://ws.example", &Default::default())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_relay_change_trips_snapshot_even_for_stored_record_relay() {
|
||||
// The legacy per-record relay pin is ignored (#2122): every record spawns
|
||||
|
||||
Reference in New Issue
Block a user