use octl_core::Kind;
const RUN_ID_SENTINEL: &str = "{RUN_ID}";
const PI_RESEARCH_PREAMBLE_TEMPLATE: &str = r#"# Operating note — pi research worker (read first)
You are an autonomous **research** worker launched by `orchestratectl` inside a
dedicated git worktree. Your deliverable is a **sourced markdown report** committed
to this repo (typically `research/<slug>.md`). Work directly with the shell, your
editor, and your web tools.
You do **not** have Claude Code's Skill tool, sub-agent / Agent tool, MCP
connectors, or any `/worktree-*` / `/llm-*` slash commands. The task brief below
was written for a Claude worker and may reference them. Translate, and never try to
invoke them:
- `/worktree-merge`, `/complex-rebase`, "merge yourself back", "self-merge" → run
the **Closing** steps at the end of this note. The `orchestratectl run merge`
call there is the entire merge-and-report step.
- `/llm-review`, `/assess-findings`, "spawn a sub-agent", "use the Skill/Agent
tool" → you have no such tool; skip it and either do the equivalent yourself
in-line or record it as a follow-up in the report's `spinoff_proposals`.
- Any other `/name` slash command → it is Claude-Code-only. Ignore it and use the
plain CLI/bash equivalent.
## Closing (mandatory — this is how the run ends)
Your run id is `{RUN_ID}` — it is already filled into the commands below. Do not
try to rediscover it.
Once the report file is written **and committed**, close the loop:
1. Write the terminal report. **Replace `<one-line outcome>` with a real one-line
summary of your findings**, and populate the arrays if you have follow-ups
(leave them `[]` otherwise). Run exactly:
```bash
cat > /tmp/node-report-{RUN_ID}.json <<'JSON'
{
"success": true,
"summary": "<one-line outcome>",
"discussion_items": [],
"spinoff_proposals": [],
"wrap_up_recommendations": []
}
JSON
```
2. Merge and report in one call:
```bash
orchestratectl run merge {RUN_ID} --report-file /tmp/node-report-{RUN_ID}.json
```
`orchestratectl run merge` rebases + merges this worktree's branch into its source
branch and submits the terminal report in the same call; the supervisor then tears
down the worktree, tmux window, and branch. Do **not** run `git worktree remove`,
`git branch -d`, or `tmux kill-window` yourself.
On a merge conflict the call exits non-zero with `error.code: "merge_failed"` and
submits **no** report. Resolve the conflict, commit, then re-run **only** the
`orchestratectl run merge` command from step 2 — the report file from step 1 is
already on disk, so do not recreate it.
---
The original task brief follows.
"#;
#[must_use]
pub fn worker_prompt_preamble(harness: &str, kind: Kind, run_id: &str) -> Option<String> {
match (harness, kind) {
("pi", Kind::Research) => {
Some(PI_RESEARCH_PREAMBLE_TEMPLATE.replace(RUN_ID_SENTINEL, run_id))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
const RUN_ID: &str = "01JXAAAA0000BBBBCCCCDDDDEE";
#[test]
fn claude_never_gets_a_preamble() {
for kind in [Kind::Research, Kind::Spinoff, Kind::FanOut] {
assert!(
worker_prompt_preamble("claude", kind, RUN_ID).is_none(),
"claude must pass the prompt through unchanged for {kind:?}"
);
}
}
#[test]
fn pi_research_gets_the_shim() {
let p =
worker_prompt_preamble("pi", Kind::Research, RUN_ID).expect("pi research shim present");
assert!(p.contains("pi research worker"));
assert!(p.contains("/worktree-merge"));
assert!(p.contains("orchestratectl run merge"));
assert!(p.contains("/llm-review"));
assert!(p.trim_end().ends_with("The original task brief follows."));
}
#[test]
fn pi_research_templates_the_exact_run_id_and_leaves_no_sentinel() {
let p = worker_prompt_preamble("pi", Kind::Research, RUN_ID).unwrap();
assert!(p.contains(&format!("orchestratectl run merge {RUN_ID} --report-file")));
assert!(p.contains(&format!("/tmp/node-report-{RUN_ID}.json")));
assert!(
!p.contains(RUN_ID_SENTINEL),
"sentinel must be fully substituted"
);
assert!(
!p.contains("~/.orchestratectl/runs/"),
"must not fall back to ls/grep run-id discovery"
);
}
#[test]
fn pi_research_closing_uses_a_quoted_heredoc() {
let p = worker_prompt_preamble("pi", Kind::Research, RUN_ID).unwrap();
assert!(p.contains("<<'JSON'"), "heredoc delimiter must be quoted");
assert!(
!p.contains("<<JSON\n"),
"an unquoted heredoc must not appear"
);
}
#[test]
fn pi_non_research_kinds_are_out_of_scope() {
for kind in [Kind::Spinoff, Kind::FanOut] {
assert!(
worker_prompt_preamble("pi", kind, RUN_ID).is_none(),
"only research is in scope for the pi shim, not {kind:?}"
);
}
}
#[test]
fn unknown_harness_gets_no_preamble() {
assert!(worker_prompt_preamble("aider", Kind::Research, RUN_ID).is_none());
}
}