Add orch Codex launch bridge workflow

This commit is contained in:
2026-03-20 18:28:48 +08:00
parent cf3c3cbe60
commit 7840b2767f
11 changed files with 523 additions and 3 deletions
+36 -2
View File
@@ -33,6 +33,7 @@ Use the bundled `./assets/orch` CLI to control leader-side orchestration through
- Prefer `--json` whenever another agent or script will read the output.
- Initialize a new database path once through the bundled `inbox init` command before the first real run.
- Use `status` as the main operational view. It reconciles worker thread state first, then returns run, task, latest-attempt, and latest-message context.
- For Codex worker launch, standardize the handoff through `./assets/orch-worker-brief` instead of improvising the worker prompt every time.
- Use this skill for leader-side scheduling and control-plane actions, not worker-side lease or progress updates.
## Rules
@@ -56,6 +57,36 @@ Use the bundled `./assets/orch` CLI to control leader-side orchestration through
5. Launch or reuse a separate worker runtime or worker agent that uses `skills/inbox/` against the same DB path.
6. Use `status`, `wait`, `blocked`, `answer`, `retry`, `reassign`, and `cleanup` to operate the run until the required tasks are complete.
## Dispatch-And-Launch Workflow
Use this when the leader wants durable `orch` state plus a worker sub-agent launched from the current Codex thread.
1. save the dispatch output:
```bash
./assets/orch --db ./coord.db --json dispatch --run RUN_ID --task TASK_ID > TMPDIR/dispatch.json
```
2. render a standardized worker brief:
```bash
./assets/orch-worker-brief --dispatch-json TMPDIR/dispatch.json --db ./coord.db > TMPDIR/worker-brief.txt
```
3. spawn one worker sub-agent with:
- `skills/inbox/`
- the generated worker brief
- the repo defaults from `AGENTS.md`
- model `gpt-5.4`
- reasoning effort `xhigh`
- `fork_context: true` first
4. keep the leader on `orch wait`, `orch status`, `orch blocked`, `orch answer`, `orch retry`, or `orch reassign`
See:
- `./references/leader-dispatch-and-launch-checklist.md`
- `./references/worker-brief-template.md`
## Worker Handoff Contract
- The worker side should use `skills/inbox/`, not this skill.
@@ -72,8 +103,10 @@ Use the bundled `./assets/orch` CLI to control leader-side orchestration through
./assets/orch --db ./coord.db --json task add --run blog_mvp_001 --task T2 --title "Summarize flaky tests" --summary "Read logs and report next steps" --default-to qa-worker --acceptance-json '{"kind":"analysis"}'
./assets/orch --db ./coord.db --json dep add --run blog_mvp_001 --task T2 --depends-on T1
./assets/orch --db ./coord.db --json ready --run blog_mvp_001
./assets/orch --db ./coord.db --json dispatch --run blog_mvp_001 --task T1 --to foundation-worker --base-ref main --workspace-root .orch/worktrees --strict-worktree --body-file tasks/t1.md
./assets/orch --db ./coord.db --json dispatch --run blog_mvp_001 --task T2 --to qa-worker --body "Read the failing test logs and summarize the root cause."
./assets/orch --db ./coord.db --json dispatch --run blog_mvp_001 --task T1 --to foundation-worker --base-ref main --workspace-root .orch/worktrees --strict-worktree --body-file tasks/t1.md > /tmp/t1-dispatch.json
./assets/orch-worker-brief --dispatch-json /tmp/t1-dispatch.json --db ./coord.db > /tmp/t1-worker-brief.txt
./assets/orch --db ./coord.db --json dispatch --run blog_mvp_001 --task T2 --to qa-worker --body "Read the failing test logs and summarize the root cause." > /tmp/t2-dispatch.json
./assets/orch-worker-brief --dispatch-json /tmp/t2-dispatch.json --db ./coord.db > /tmp/t2-worker-brief.txt
./assets/orch --db ./coord.db --json status --run blog_mvp_001
./assets/orch --db ./coord.db --json wait --run blog_mvp_001 --for task_blocked,task_done,task_failed --after-event 0 --timeout-seconds 900
./assets/orch --db ./coord.db --json blocked --run blog_mvp_001
@@ -104,6 +137,7 @@ Use the bundled `./assets/orch` CLI to control leader-side orchestration through
- `dispatch` supports `--repo-path`, `--workspace-root`, `--strict-worktree`, and `--base-ref` for worktree-backed code execution.
- When worktree flags are omitted, code-like task metadata can still auto-enable strict worktree mode. Non-code tasks stay on the normal thread-only path.
- `./assets/orch-worker-brief` is the supported way to turn a saved dispatch JSON response into a stable worker prompt for a spawned sub-agent.
- `answer` supports `--payload-json` for structured decisions, not just freeform text.
- `status` is the full run view; `run show` is the lighter aggregate view.
- If the bundled binary cannot execute on the current host, stop and report the compatibility issue instead of guessing a replacement path or workflow.
+1 -1
View File
@@ -1,7 +1,7 @@
interface:
display_name: "Orch CLI"
short_description: "Leader-side orchestration CLI"
default_prompt: "Use $orch to manage leader-side orchestration runs through the bundled orch CLI and a SQLite orchestration database. Treat it as a control plane only: dispatch creates attempts and inbox threads, while separate workers consume them through inbox."
default_prompt: "Use $orch to manage leader-side orchestration runs through the bundled orch CLI and a SQLite orchestration database. Treat it as a control plane only: dispatch creates attempts and inbox threads, while separate workers consume them through inbox. When you choose to launch a worker sub-agent from the current Codex thread, standardize the handoff with ./assets/orch-worker-brief and keep the worker on skills/inbox only."
policy:
allow_implicit_invocation: true
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly DEFAULT_INBOX_SKILL_PATH="$(cd "${SCRIPT_DIR}/../../inbox" && pwd)"
dispatch_json=""
db_path=""
inbox_skill_path="${DEFAULT_INBOX_SKILL_PATH}"
extra_instructions_file=""
usage() {
cat <<'EOF'
Usage: orch-worker-brief --dispatch-json PATH --db PATH [--inbox-skill-path PATH] [--extra-instructions-file PATH]
Read an `orch dispatch --json` response and render a standardized worker prompt
for a Codex sub-agent that should execute the assigned inbox thread.
Required:
--dispatch-json PATH Path to the saved `orch dispatch --json` response
--db PATH Shared SQLite DB path for both orch and inbox
Optional:
--inbox-skill-path PATH Path to the inbox skill bundle. Defaults to the
sibling project-local skill at skills/inbox
--extra-instructions-file Append extra operator-specific instructions
-h, --help Show this help text
EOF
}
require_command() {
local cmd="$1"
if ! command -v "${cmd}" >/dev/null 2>&1; then
printf 'missing required command: %s\n' "${cmd}" >&2
exit 1
fi
}
while [ "$#" -gt 0 ]; do
case "$1" in
--dispatch-json)
dispatch_json="${2:-}"
shift 2
;;
--db)
db_path="${2:-}"
shift 2
;;
--inbox-skill-path)
inbox_skill_path="${2:-}"
shift 2
;;
--extra-instructions-file)
extra_instructions_file="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
printf 'unknown argument: %s\n' "$1" >&2
usage >&2
exit 1
;;
esac
done
require_command jq
if [ -z "${dispatch_json}" ] || [ -z "${db_path}" ]; then
usage >&2
exit 1
fi
if [ ! -f "${dispatch_json}" ]; then
printf 'dispatch json not found: %s\n' "${dispatch_json}" >&2
exit 1
fi
if [ -n "${extra_instructions_file}" ] && [ ! -f "${extra_instructions_file}" ]; then
printf 'extra instructions file not found: %s\n' "${extra_instructions_file}" >&2
exit 1
fi
run_id="$(jq -r '.data.attempt.run_id // .data.task.run_id // empty' "${dispatch_json}")"
task_id="$(jq -r '.data.attempt.task_id // .data.task.task_id // empty' "${dispatch_json}")"
attempt_no="$(jq -r '.data.attempt.attempt_no // empty' "${dispatch_json}")"
assigned_to="$(jq -r '.data.attempt.assigned_to // .data.task.default_to // empty' "${dispatch_json}")"
thread_id="$(jq -r '.data.attempt.thread_id // .data.thread.thread_id // empty' "${dispatch_json}")"
worktree_path="$(jq -r '.data.attempt.worktree_path // empty' "${dispatch_json}")"
base_ref="$(jq -r '.data.attempt.base_ref // empty' "${dispatch_json}")"
task_title="$(jq -r '.data.task.title // empty' "${dispatch_json}")"
task_summary="$(jq -r '.data.task.summary // empty' "${dispatch_json}")"
leader_body="$(jq -r '.data.message.body // empty' "${dispatch_json}")"
if [ -z "${run_id}" ] || [ -z "${task_id}" ] || [ -z "${assigned_to}" ] || [ -z "${thread_id}" ]; then
printf 'dispatch json is missing one or more required fields: run_id, task_id, assigned_to, thread_id\n' >&2
exit 1
fi
mode="analysis"
if [ -n "${worktree_path}" ]; then
mode="code"
fi
cat <<EOF
Use \$inbox at ${inbox_skill_path} to act as ${assigned_to} on SQLite DB ${db_path}. Only coordinate through the bundled inbox CLI from the skill. Do not use ordinary chat to coordinate with the leader. Do not use orch.
You are assigned one existing attempt:
- run_id: ${run_id}
- task_id: ${task_id}
- attempt_no: ${attempt_no}
- assigned_to: ${assigned_to}
- thread_id: ${thread_id}
- execution_mode: ${mode}
EOF
if [ -n "${task_title}" ]; then
printf '%s\n' "- task_title: ${task_title}"
fi
if [ -n "${task_summary}" ]; then
printf '%s\n' "- task_summary: ${task_summary}"
fi
if [ -n "${base_ref}" ]; then
printf '%s\n' "- base_ref: ${base_ref}"
fi
if [ -n "${worktree_path}" ]; then
printf '%s\n' "- worktree_path: ${worktree_path}"
fi
cat <<'EOF'
Required workflow:
1. Fetch or inspect the assigned thread and claim exactly the listed thread_id.
2. Inspect the thread with show before making assumptions about the task body or payload.
3. Send one in_progress update when real work starts.
4. If blocked, send one precise blocked question with update --status blocked and then use wait-reply.
5. Finish with done on success or fail on failure.
6. Stop after reporting the handled thread_id and a concise outcome summary.
Rules:
- Do not claim any other thread.
- Keep all coordination in inbox messages, not ordinary chat.
- Do not change the assigned_to, thread_id, or worktree assignment yourself.
EOF
if [ -n "${worktree_path}" ]; then
cat <<EOF
- Perform repository work only inside ${worktree_path}.
- Do not modify the leader's primary checkout outside the assigned worktree.
EOF
fi
if [ -n "${leader_body}" ]; then
printf '\nLeader dispatch body:\n%s\n' "${leader_body}"
fi
if [ -n "${extra_instructions_file}" ]; then
printf '\nExtra instructions:\n'
cat "${extra_instructions_file}"
printf '\n'
fi
@@ -0,0 +1,52 @@
# Leader Dispatch-And-Launch Checklist
Use this checklist when the leader wants durable `orch` scheduling plus a Codex sub-agent worker.
## Goal
Keep `orch` as the control plane and use a leader-side bridge to launch a worker sub-agent only after `dispatch` succeeds.
## Recommended Flow
1. initialize the shared DB once through `skills/inbox/assets/inbox --db PATH --json init`
2. create the run and tasks with `run init`, `task add`, and `dep add`
3. find dispatchable work with `ready`
4. save the dispatch response:
```bash
./assets/orch --db ./coord.db --json dispatch --run RUN_ID --task TASK_ID > TMPDIR/dispatch.json
```
5. render a standardized worker brief:
```bash
./assets/orch-worker-brief --dispatch-json TMPDIR/dispatch.json --db ./coord.db > TMPDIR/worker-brief.txt
```
6. spawn one worker sub-agent with:
- injected `skills/inbox/`
- the generated worker brief
- the repository defaults from `AGENTS.md`
- model: `gpt-5.4`
- reasoning effort: `xhigh`
- `fork_context: true` first
7. while the worker runs, keep the leader on:
- `orch wait`
- `orch status`
- `orch blocked`
- `orch answer`
- `orch retry`
- `orch reassign`
## Worker Launch Rules
- the worker should use `skills/inbox/`, not `skills/orch/`
- the worker should claim only the exact `thread_id` from the dispatch result
- if `worktree_path` exists, the worker should do all repository writes only inside that worktree
- the worker should send progress, blocked questions, success, and failure through `inbox`
## Why This Layer Exists
- `orch` remains portable and durable
- worker launch stays outside the core CLI
- the same dispatch contract can later support Codex sub-agents, `codex exec`, daemons, or operator UI launchers
@@ -0,0 +1,41 @@
# Worker Brief Template
`assets/orch-worker-brief` renders a concrete prompt in this shape.
Use it when a leader has already dispatched a task and now wants to launch a worker sub-agent without hand-writing the thread handoff every time.
```text
Use $inbox at INBOX_SKILL_PATH to act as ASSIGNED_TO on SQLite DB DB_PATH. Only coordinate through the bundled inbox CLI from the skill. Do not use ordinary chat to coordinate with the leader. Do not use orch.
You are assigned one existing attempt:
- run_id: RUN_ID
- task_id: TASK_ID
- attempt_no: ATTEMPT_NO
- assigned_to: ASSIGNED_TO
- thread_id: THREAD_ID
- execution_mode: analysis|code
- task_title: ...
- task_summary: ...
- base_ref: ...
- worktree_path: ...
Required workflow:
1. Fetch or inspect the assigned thread and claim exactly the listed thread_id.
2. Inspect the thread with show before making assumptions about the task body or payload.
3. Send one in_progress update when real work starts.
4. If blocked, send one precise blocked question with update --status blocked and then use wait-reply.
5. Finish with done on success or fail on failure.
6. Stop after reporting the handled thread_id and a concise outcome summary.
Rules:
- Do not claim any other thread.
- Keep all coordination in inbox messages, not ordinary chat.
- Do not change the assigned_to, thread_id, or worktree assignment yourself.
- If worktree_path exists, perform repository work only inside that path.
```
## Notes
- The template is intentionally worker-only. Leader control stays with `orch`.
- The generated brief should be passed into a spawned worker sub-agent together with `skills/inbox/`.
- Keep the main-thread launch parameters in the leader workflow, not in the worker brief.