fix(provision): let callers declare write access instead of guessing from the role name
default_risk_profile_for_role decides whether a claw gets file edits, git and shell by substring-matching its role against a fixed keyword list. On the planner path that role string is free text the model invented for this proposal, so a model's choice of wording silently decided tool access: a proposed "implementation_lead" matches no keyword, lands research_readonly, and then fails every file edit for a reason invisible from the role name. TeamMemberInput and the planner's member schema now carry `needs_write`, and resolve_risk_profile prefers it over the guess. The planner prompt asks for it per member and says to grant write only to members that produce code or commits. Absent (older clients, autoprovision, a model that omitted the field) falls back to the old guess, so nothing changes for callers that don't set it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6926107e4f
commit
b1bdfbbf87
@@ -27,6 +27,13 @@ pub struct TeamMemberInput {
|
||||
pub system_prompt: String,
|
||||
#[serde(default)]
|
||||
pub accent: String,
|
||||
/// Whether this member needs write access (file edits, git, shell) rather
|
||||
/// than read-only research tools.
|
||||
///
|
||||
/// `None` falls back to guessing from the role name, which is what we used
|
||||
/// to do unconditionally — see `resolve_risk_profile`.
|
||||
#[serde(default)]
|
||||
pub needs_write: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -129,7 +136,7 @@ pub(crate) async fn build_team_with_lifecycle(
|
||||
)
|
||||
.await?;
|
||||
let claw_id = agent.id.as_uuid();
|
||||
let risk = RuntimeProvisioner::default_risk_profile_for_role(&m.role);
|
||||
let risk = RuntimeProvisioner::resolve_risk_profile(&m.role, m.needs_write);
|
||||
// Ad-hoc team-wizard teams aren't mission-bound, so they use the
|
||||
// default per-agent workspace under <install>/agents/<alias>/workspace/.
|
||||
provisioner
|
||||
@@ -712,6 +719,10 @@ pub async fn auto_provision(
|
||||
model: model.clone(),
|
||||
system_prompt: r.system_prompt.trim().to_string(),
|
||||
accent: String::new(),
|
||||
// The autoprovision roster schema doesn't declare access yet, so
|
||||
// this path keeps the role-name guess rather than silently
|
||||
// changing what it grants.
|
||||
needs_write: None,
|
||||
})
|
||||
.collect();
|
||||
let team_name = format!("Auto · {}", body.title.trim());
|
||||
|
||||
Reference in New Issue
Block a user