feat: add multi-runtime support (coord run, prdy --codex) and next-task capsule

- coord/prdy subcommands now accept --claude/--codex runtime flags
- New `mosaic coord run` generates continuation context and launches
  selected runtime, replacing manual copy/paste workflow
- Next-task capsule (.mosaic/orchestrator/next-task.json) provides
  machine-readable execution context for deterministic session launches
- Codex strict orchestrator profile added to runtime/codex/RUNTIME.md
- Orchestrator protocol updated with between-session run flow
- New smoke-test.sh for orchestration behavior verification

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-23 18:27:09 -06:00
parent fbf74c2736
commit abead17e0e
12 changed files with 517 additions and 41 deletions

View File

@@ -11,6 +11,7 @@ MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
ORCH_SUBDIR=".mosaic/orchestrator"
MISSION_FILE="mission.json"
SESSION_LOCK_FILE="session.lock"
NEXT_TASK_FILE="next-task.json"
MANIFEST_FILE="docs/MISSION-MANIFEST.md"
TASKS_MD="docs/TASKS.md"
SCRATCHPAD_DIR="docs/scratchpads"
@@ -42,6 +43,30 @@ _require_jq() {
fi
}
coord_runtime() {
local runtime="${MOSAIC_COORD_RUNTIME:-claude}"
case "$runtime" in
claude|codex) echo "$runtime" ;;
*) echo "claude" ;;
esac
}
coord_launch_command() {
local runtime
runtime="$(coord_runtime)"
echo "mosaic $runtime"
}
coord_run_command() {
local runtime
runtime="$(coord_runtime)"
if [[ "$runtime" == "claude" ]]; then
echo "mosaic coord run"
else
echo "mosaic coord run --$runtime"
fi
}
# ─── Project / state file access ────────────────────────────────────────────
# Return the orchestrator directory for a project
@@ -56,6 +81,11 @@ mission_path() {
echo "$(orch_dir "$project")/$MISSION_FILE"
}
next_task_capsule_path() {
local project="${1:-.}"
echo "$(orch_dir "$project")/$NEXT_TASK_FILE"
}
# Exit with error if mission.json is missing or inactive
require_mission() {
local project="${1:-.}"
@@ -358,6 +388,113 @@ milestone_name() {
jq -r --arg id "$mid" '.milestones[] | select(.id == $id) | .name // empty' "$mp"
}
# ─── Next-task capsule helpers ───────────────────────────────────────────────
write_next_task_capsule() {
local project="${1:-.}"
local runtime="${2:-claude}"
local mission_id="${3:-}"
local mission_name="${4:-}"
local project_path="${5:-}"
local quality_gates="${6:-}"
local current_ms_id="${7:-}"
local current_ms_name="${8:-}"
local next_task="${9:-}"
local tasks_done="${10:-0}"
local tasks_total="${11:-0}"
local pct="${12:-0}"
local current_branch="${13:-}"
_require_jq || return 1
mkdir -p "$(orch_dir "$project")"
local payload
payload="$(jq -n \
--arg generated_at "$(iso_now)" \
--arg runtime "$runtime" \
--arg mission_id "$mission_id" \
--arg mission_name "$mission_name" \
--arg project_path "$project_path" \
--arg quality_gates "$quality_gates" \
--arg current_ms_id "$current_ms_id" \
--arg current_ms_name "$current_ms_name" \
--arg next_task "$next_task" \
--arg current_branch "$current_branch" \
--arg tasks_done "$tasks_done" \
--arg tasks_total "$tasks_total" \
--arg pct "$pct" \
'{
generated_at: $generated_at,
runtime: $runtime,
mission_id: $mission_id,
mission_name: $mission_name,
project_path: $project_path,
quality_gates: $quality_gates,
current_milestone: {
id: $current_ms_id,
name: $current_ms_name
},
next_task: $next_task,
progress: {
tasks_done: ($tasks_done | tonumber),
tasks_total: ($tasks_total | tonumber),
pct: ($pct | tonumber)
},
current_branch: $current_branch
}')"
write_json "$(next_task_capsule_path "$project")" "$payload"
}
build_codex_strict_kickoff() {
local project="${1:-.}"
local continuation_prompt="${2:-}"
_require_jq || return 1
local capsule_path
capsule_path="$(next_task_capsule_path "$project")"
local capsule='{}'
if [[ -f "$capsule_path" ]]; then
capsule="$(cat "$capsule_path")"
fi
local mission_id next_task project_path quality_gates
mission_id="$(echo "$capsule" | jq -r '.mission_id // "unknown"')"
next_task="$(echo "$capsule" | jq -r '.next_task // "none"')"
project_path="$(echo "$capsule" | jq -r '.project_path // "."')"
quality_gates="$(echo "$capsule" | jq -r '.quality_gates // "none"')"
cat <<EOF
Now initiating Orchestrator mode...
STRICT EXECUTION PROFILE FOR CODEX (HARD GATE)
- Do NOT ask clarifying questions before your first tool actions unless a Mosaic escalation trigger is hit.
- Your first actions must be reading mission state files in order.
- Treat the next-task capsule as authoritative execution input.
REQUIRED FIRST ACTIONS (IN ORDER)
1. Read ~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md
2. Read docs/MISSION-MANIFEST.md
3. Read docs/scratchpads/${mission_id}.md
4. Read docs/TASKS.md
5. Begin execution on next task: ${next_task}
WORKING CONTEXT
- Project: ${project_path}
- Quality gates: ${quality_gates}
- Capsule file: .mosaic/orchestrator/next-task.json
Task capsule (JSON):
\`\`\`json
${capsule}
\`\`\`
Continuation prompt:
${continuation_prompt}
EOF
}
# Get next milestone after the given one
next_milestone_id() {
local project="${1:-.}"