worker: preserve prompt overrides in internal children

This commit is contained in:
2026-08-07 02:15:00 +09:00
parent ee75272917
commit e6703ed18a
2 changed files with 33 additions and 3 deletions
+6 -1
View File
@@ -251,6 +251,7 @@ struct PackFile {
/// `$yoi` / `$user` / `$workspace`.
pub struct PromptCatalog {
env: Environment<'static>,
loader: PromptLoader,
}
impl std::fmt::Debug for PromptCatalog {
@@ -260,6 +261,10 @@ impl std::fmt::Debug for PromptCatalog {
}
impl PromptCatalog {
pub(crate) fn loader(&self) -> PromptLoader {
self.loader.clone()
}
/// Builtin-only catalog. All `{% include %}` references must resolve
/// through `$yoi` (user/workspace prefixes are unavailable).
pub fn builtins_only() -> Result<Arc<Self>, CatalogError> {
@@ -483,7 +488,7 @@ fn build_catalog(
})?;
}
Ok(PromptCatalog { env })
Ok(PromptCatalog { env, loader })
}
#[cfg(test)]
+27 -2
View File
@@ -226,6 +226,7 @@ pub struct SubWorkerSpawnTool {
/// child config from reusable fields here, and selected profiles are
/// merged into the same internal handoff shape before launch.
spawner_manifest: WorkerManifest,
prompt_loader: PromptLoader,
/// Compact selector list shared by tool description and diagnostics.
available_profiles: AvailableProfiles,
/// Spawner's runtime scope. After a successful spawn, the
@@ -259,6 +260,7 @@ impl SubWorkerSpawnTool {
spawner_cwd: PathBuf,
registry: Arc<SpawnedWorkerRegistry>,
spawner_manifest: WorkerManifest,
prompt_loader: PromptLoader,
available_profiles: AvailableProfiles,
spawner_scope: SharedScope,
delegation_scope: DelegationScope,
@@ -272,6 +274,7 @@ impl SubWorkerSpawnTool {
spawner_cwd,
registry,
spawner_manifest,
prompt_loader,
available_profiles,
spawner_scope,
delegation_scope,
@@ -340,7 +343,7 @@ impl Tool for SubWorkerSpawnTool {
let mut child = Worker::<Box<dyn llm_engine::llm_client::LlmClient>, EphemeralSessionStore>::from_internal_manifest_with_context(
child_manifest,
store.clone(),
PromptLoader::builtins_only(),
self.prompt_loader.clone(),
self.workspace_context.clone(),
filesystem_authority,
self.internal_client_override
@@ -803,6 +806,7 @@ fn sub_worker_spawn_tool_impl(
spawner_cwd.clone(),
registry.clone(),
spawner_manifest.clone(),
prompts.loader(),
available_profiles,
spawner_scope.clone(),
DelegationScope::from_config(&spawner_manifest.delegation_scope)
@@ -858,6 +862,15 @@ mod tests {
);
let calls = Arc::new(AtomicUsize::new(0));
let observed_parent_write_revoked = Arc::new(std::sync::atomic::AtomicBool::new(false));
let observed_instruction_override = Arc::new(std::sync::atomic::AtomicBool::new(false));
let workspace_prompts = runtime.path().join("workspace-prompts");
std::fs::create_dir_all(&workspace_prompts).unwrap();
std::fs::write(
workspace_prompts.join("custom-reviewer.md"),
"WORKSPACE REVIEWER OVERRIDE",
)
.unwrap();
let prompt_loader = PromptLoader::new(None, Some(workspace_prompts));
let parent_notifies = crate::ipc::notify_buffer::NotifyBuffer::new();
let tool = SubWorkerSpawnTool::new(
"parent".into(),
@@ -868,6 +881,7 @@ mod tests {
workspace_root.clone(),
registry.clone(),
manifest.clone(),
prompt_loader,
AvailableProfiles::discover(&workspace_root),
spawner_scope.clone(),
DelegationScope::from_config(&manifest.delegation_scope).unwrap(),
@@ -877,10 +891,12 @@ mod tests {
parent_scope: spawner_scope.clone(),
delegated_path: workspace_root.clone(),
observed_parent_write_revoked: observed_parent_write_revoked.clone(),
observed_instruction_override: observed_instruction_override.clone(),
}));
let input = serde_json::json!({
"name": "reviewer-child",
"profile": "builtin:reviewer",
"instruction": "$workspace/custom-reviewer",
"task": "review immutable commit",
"scope": [{
"target": workspace_root.clone(),
@@ -909,6 +925,7 @@ mod tests {
);
assert_eq!(calls.load(Ordering::SeqCst), 1);
assert!(observed_parent_write_revoked.load(Ordering::SeqCst));
assert!(observed_instruction_override.load(Ordering::SeqCst));
assert_eq!(parent_notifies.len(), 1);
assert!(!runtime.path().join("reviewer-child/sock").exists());
@@ -1080,6 +1097,7 @@ mod tests {
parent_scope: SharedScope,
delegated_path: PathBuf,
observed_parent_write_revoked: Arc<std::sync::atomic::AtomicBool>,
observed_instruction_override: Arc<std::sync::atomic::AtomicBool>,
}
#[async_trait]
@@ -1090,7 +1108,7 @@ mod tests {
async fn stream(
&self,
_request: Request,
request: Request,
) -> Result<Pin<Box<dyn Stream<Item = Result<LlmEvent, ClientError>> + Send>>, ClientError>
{
self.calls.fetch_add(1, Ordering::SeqCst);
@@ -1101,6 +1119,13 @@ mod tests {
.is_writable(&self.delegated_path),
Ordering::SeqCst,
);
self.observed_instruction_override.store(
request
.system_prompt
.as_deref()
.is_some_and(|prompt| prompt.contains("WORKSPACE REVIEWER OVERRIDE")),
Ordering::SeqCst,
);
Ok(Box::pin(futures::stream::iter(vec![
Ok(LlmEvent::text_block_start(0)),
Ok(LlmEvent::text_delta(0, "reviewed")),