prompt: confine orchestrator integration to orchestration branch

This commit is contained in:
2026-06-12 20:49:51 +09:00
parent 0bcf90680c
commit 47c82103cd
7 changed files with 102 additions and 132 deletions
+34 -64
View File
@@ -673,7 +673,6 @@ fn build_launch_prompt(
fn append_workspace_routing_context(out: &mut String, context: &TicketRoleLaunchContext) {
let original_workspace_root = context.original_workspace_root();
let target_workspace_root = context.target_workspace_root();
let implementation_worktree_root = context.implementation_worktree_root();
let should_emit = context.original_workspace_root.is_some()
|| context.target_workspace_root.is_some()
@@ -701,14 +700,15 @@ fn append_workspace_routing_context(out: &mut String, context: &TicketRoleLaunch
"implementation_worktree_root",
&implementation_worktree_root.display().to_string(),
);
push_bounded_bullet(
out,
"merge_target_workspace_root",
&target_workspace_root.display().to_string(),
);
out.push_str(
"- Treat `role_workspace_root` as the launched role runtime workspace/Ticket backend root. Create implementation worktrees under `implementation_worktree_root`, not relative to the role cwd, and run merge-completion against `merge_target_workspace_root`. `implementation_worktree_root` is placement authority only; implementation branches are based on the Orchestrator workspace current HEAD / orchestration branch HEAD, not the merge-target workspace or `develop` HEAD.\n",
);
if context.role == TicketRole::Orchestrator {
out.push_str(
"- Treat `role_workspace_root` / `role_cwd` as the Orchestrator workspace on the orchestration branch. Use `implementation_worktree_root` only as the placement root for child implementation worktrees; do not operate on `original_workspace_root` itself. Root/original workspace reads, writes, validation, cleanup, and git operations are prohibited. Create implementation branches from the Orchestrator workspace current HEAD / orchestration branch HEAD and integrate reviewed work back into that orchestration branch automatically.\n",
);
} else {
out.push_str(
"- Treat `role_workspace_root` as the launched role runtime workspace/Ticket backend root. Create implementation worktrees under `implementation_worktree_root`, not relative to the role cwd, and run role work against the recorded workspace routing context.\n",
);
}
}
fn append_role_execution_guidance(
@@ -1274,11 +1274,11 @@ workflow = "ticket-review-workflow"
assert!(orchestrator_text.contains("branch-local reviewer verdicts"));
assert!(orchestrator_text.contains("binding decisions/invariants"));
assert!(orchestrator_text.contains("not unrecorded preferred tactics"));
assert!(orchestrator_text.contains("merge-ready dossier"));
assert!(orchestrator_text.contains("integration outcome"));
assert!(orchestrator_text.contains(
"Stop at a merge-ready dossier only when merge-completion authority is absent"
"integrate the implementation branch into the orchestration branch automatically"
));
assert!(orchestrator_text.contains("continue through merge, validation, Ticket close"));
assert!(orchestrator_text.contains("Root/original workspace reads, writes, validation, cleanup, and git operations are prohibited"));
let mut coder = TicketRoleLaunchContext::new(temp.path(), TicketRole::Coder);
coder.ticket = Some(TicketRef::id("20260605-190330-ticket-role-pod-launcher"));
@@ -1314,18 +1314,20 @@ workflow = "ticket-review-workflow"
assert!(reviewer_text.contains("read-only by default"));
assert!(reviewer_text.contains("recorded intent, binding decisions/invariants"));
assert!(reviewer_text.contains("not unrecorded preferred tactics"));
assert!(reviewer_text.contains("branch-local reviewer verdict"));
assert!(reviewer_text.contains("Do not record final main-branch Ticket approval"));
assert!(reviewer_text.contains("Orchestrator-side integration"));
assert!(
reviewer_text
.contains("Do not merge, close, push, operate on the root/original workspace")
);
}
#[test]
fn orchestrator_prompt_covers_merge_completion_authority_and_dossier_boundaries() {
fn orchestrator_prompt_covers_orchestration_branch_integration_boundary() {
let temp = TempDir::new().unwrap();
write_builtin_role_config(temp.path(), &[TicketRole::Orchestrator]);
let mut orchestrator = TicketRoleLaunchContext::new(temp.path(), TicketRole::Orchestrator);
orchestrator.ticket = Some(TicketRef::id("orchestrator-merge-completion"));
orchestrator.intent_packet =
Some("Complete an already-reviewed merge-ready Ticket.".into());
orchestrator.ticket = Some(TicketRef::id("orchestrator-integration"));
orchestrator.intent_packet = Some("Complete an already-reviewed Ticket.".into());
orchestrator.validation = vec!["cargo test -p client ticket_role --lib".into()];
orchestrator = orchestrator
.with_original_workspace_root(temp.path().join("original"))
@@ -1349,56 +1351,24 @@ workflow = "ticket-review-workflow"
assert!(text.contains("Workspace routing context:"));
assert!(text.contains("role_workspace_root"));
assert!(text.contains("implementation_worktree_root"));
assert!(text.contains("merge_target_workspace_root"));
assert!(text.contains("not relative to the role cwd"));
assert!(text.contains("`implementation_worktree_root` is placement authority only"));
assert!(text.contains("implementation branches are based on the Orchestrator workspace current HEAD / orchestration branch HEAD"));
assert!(text.contains("not the merge-target workspace or `develop` HEAD"));
assert!(text.contains("Orchestrator merge-completion guidance"));
assert!(text.contains("`inprogress` Ticket with a merge-ready dossier"));
assert!(text.contains("Conservative or missing authorization mode stops at the dossier"));
assert!(text.contains("explicit user/standing policy may authorize continuing"));
assert!(text.contains("dossier branch/worktree/commits match the branch to merge"));
assert!(text.contains("independent reviewer approval exists in the dossier"));
assert!(text.contains("explicit human override decision is recorded"));
assert!(text.contains("the merge target workspace is safe"));
assert!(text.contains("unrelated dirty changes are understood"));
assert!(text.contains("dogfooding/workspace policy grants merge authority"));
assert!(text.contains("branch-local reviewer verdicts are dossier evidence"));
assert!(text.contains("final main-branch Ticket approval or close happens only during authorized merge-completion"));
assert!(text.contains("stop/reclaim coder and reviewer Pods"));
assert!(text.contains("git merge --no-ff <branch>"));
assert!(text.contains("run post-merge validation appropriate to the change"));
assert!(text.contains("Orchestrator workspace on the orchestration branch"));
assert!(text.contains("Root/original workspace reads, writes, validation, cleanup, and git operations are prohibited"));
assert!(text.contains("Create implementation branches from the Orchestrator workspace current HEAD / orchestration branch HEAD"));
assert!(
text.contains("record review, merge, and validation outcomes in the Ticket thread")
text.contains(
"integrate reviewed work back into that orchestration branch automatically"
)
);
assert!(text.contains("Orchestrator implementation integration guidance"));
assert!(
text.contains("Integrate only within the Orchestrator workspace/orchestration branch")
);
assert!(text.contains("root/original workspace is not an integration target"));
assert!(text.contains("merge or otherwise integrate that implementation branch into the orchestration branch automatically"));
assert!(text.contains(
"transition `inprogress -> done` or close according to typed Ticket workflow rules"
"Run post-integration validation from the Orchestrator workspace/orchestration branch"
));
assert!(text.contains(
"remove the merged child worktree and delete the merged branch unless explicitly kept"
));
assert!(text.contains("Required dossier fields before merge"));
assert!(text.contains("Ticket id"));
assert!(text.contains("branch/worktree"));
assert!(text.contains("commits"));
assert!(text.contains("intent/invariant check"));
assert!(text.contains("implementation summary"));
assert!(text.contains("coder/reviewer Pods"));
assert!(text.contains("blockers fixed or rejected findings with reasons"));
assert!(text.contains("validation performed"));
assert!(text.contains("residual risks"));
assert!(text.contains("dirty state"));
assert!(text.contains("parent/human decision needs if any"));
assert!(text.contains("Post-merge validation baseline"));
assert!(text.contains("focused tests from the Ticket/dossier"));
assert!(text.contains("cargo fmt --check"));
assert!(text.contains("git diff --check"));
assert!(text.contains("target/debug/yoi ticket doctor"));
assert!(text.contains("where applicable"));
assert!(text.contains("cargo check --workspace --all-targets"));
assert!(text.contains("nix build .#yoi"));
assert!(text.contains("when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it"));
assert!(text.contains("Cleanup is limited to the child implementation worktree/branch"));
}
#[test]
+8 -4
View File
@@ -2517,7 +2517,7 @@ fn orchestrator_queue_notification_message(
) -> String {
let title = ticket.title.replace(['\r', '\n'], " ");
format!(
"Workspace panel Queue for Ticket `{}`, title `{}`: human authorized Orchestrator routing; this is not an unattended scheduler. Read the Ticket and inspect current workspace state. If unblocked, record routing and transition state queued -> inprogress before any worktree/SpawnPod implementation side effects. After inprogress acceptance, use worktree-workflow for `.worktree/<task-name>` creation with tracked `.yoi` project records visible and `.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths excluded, then use multi-agent-workflow to run sibling coder/reviewer Pods (coder narrow child-worktree write scope, reviewer read-only by default) and stop at a merge-ready dossier without merge/close/final approval. If blocked, record a concise reason and leave the Ticket queued or return it to planning with the missing-information reason.",
"Workspace panel Queue for Ticket `{}`, title `{}`: human authorized Orchestrator routing; this is not an unattended scheduler. Read the Ticket and inspect current Orchestrator workspace state. If unblocked, record routing and transition state queued -> inprogress before any worktree/SpawnPod implementation side effects. After inprogress acceptance, use worktree-workflow for `.worktree/<task-name>` creation with tracked `.yoi` project records visible and `.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths excluded, then use multi-agent-workflow to run sibling coder/reviewer Pods (coder narrow child-worktree write scope, reviewer read-only by default). After reviewer approval and blocker resolution, integrate the implementation branch into the orchestration branch automatically, validate in the Orchestrator worktree, record the outcome, and clean up only child implementation worktrees/branches. Do not read, write, validate, merge, clean up, or run git operations in the root/original workspace. If blocked, record a concise reason and leave the Ticket queued or return it to planning with the missing-information reason.",
ticket.id,
title.trim()
)
@@ -3926,7 +3926,7 @@ mod tests {
assert!(message.contains("human authorized Orchestrator routing"));
assert!(message.contains("not an unattended scheduler"));
assert!(message.contains("Read the Ticket"));
assert!(message.contains("inspect current workspace state"));
assert!(message.contains("inspect current Orchestrator workspace state"));
assert!(message.contains("transition state queued -> inprogress"));
assert!(message.contains("before any worktree/SpawnPod implementation side effects"));
assert!(message.contains("After inprogress acceptance"));
@@ -3940,8 +3940,12 @@ mod tests {
assert!(message.contains("sibling coder/reviewer Pods"));
assert!(message.contains("coder narrow child-worktree write scope"));
assert!(message.contains("reviewer read-only by default"));
assert!(message.contains("merge-ready dossier"));
assert!(message.contains("without merge/close/final approval"));
assert!(message.contains(
"integrate the implementation branch into the orchestration branch automatically"
));
assert!(message.contains("validate in the Orchestrator worktree"));
assert!(message.contains("clean up only child implementation worktrees/branches"));
assert!(message.contains("Do not read, write, validate, merge, clean up, or run git operations in the root/original workspace"));
assert!(message.contains("If blocked, record a concise reason"));
assert!(message.contains("leave the Ticket queued or return it to planning"));
assert!(!message.contains("Do not start implementation directly"));