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:
109
bin/mosaic
109
bin/mosaic
@@ -53,8 +53,8 @@ Management:
|
||||
|
||||
PRD:
|
||||
prdy <subcommand> PRD creation and validation
|
||||
init Create docs/PRD.md via Claude session
|
||||
update Update existing PRD via Claude session
|
||||
init Create docs/PRD.md via guided runtime session
|
||||
update Update existing PRD via guided runtime session
|
||||
validate Check PRD completeness (bash-only)
|
||||
|
||||
Coordinator (r0):
|
||||
@@ -63,6 +63,7 @@ Coordinator (r0):
|
||||
mission Show mission progress dashboard
|
||||
status Check agent session health
|
||||
continue Generate continuation prompt
|
||||
run Generate context and launch selected runtime
|
||||
resume Crash recovery
|
||||
|
||||
Options:
|
||||
@@ -481,26 +482,59 @@ run_seq() {
|
||||
|
||||
run_coord() {
|
||||
check_mosaic_home
|
||||
local subcmd="${1:-help}"
|
||||
shift || true
|
||||
local runtime="claude"
|
||||
local runtime_flag=""
|
||||
local -a coord_args=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--claude|--codex)
|
||||
local selected_runtime="${1#--}"
|
||||
if [[ -n "$runtime_flag" ]] && [[ "$runtime" != "$selected_runtime" ]]; then
|
||||
echo "[mosaic] ERROR: --claude and --codex are mutually exclusive for 'mosaic coord'." >&2
|
||||
exit 1
|
||||
fi
|
||||
runtime="$selected_runtime"
|
||||
runtime_flag="$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
coord_args+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local subcmd="${coord_args[0]:-help}"
|
||||
if (( ${#coord_args[@]} > 1 )); then
|
||||
set -- "${coord_args[@]:1}"
|
||||
else
|
||||
set --
|
||||
fi
|
||||
|
||||
local tool_dir="$MOSAIC_HOME/tools/orchestrator"
|
||||
|
||||
case "$subcmd" in
|
||||
status|session)
|
||||
exec bash "$tool_dir/session-status.sh" "$@"
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-status.sh" "$@"
|
||||
;;
|
||||
init)
|
||||
exec bash "$tool_dir/mission-init.sh" "$@"
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-init.sh" "$@"
|
||||
;;
|
||||
mission|progress)
|
||||
exec bash "$tool_dir/mission-status.sh" "$@"
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-status.sh" "$@"
|
||||
;;
|
||||
continue|next)
|
||||
exec bash "$tool_dir/continue-prompt.sh" "$@"
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/continue-prompt.sh" "$@"
|
||||
;;
|
||||
run|start)
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-run.sh" "$@"
|
||||
;;
|
||||
smoke|test)
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/smoke-test.sh" "$@"
|
||||
;;
|
||||
resume|recover)
|
||||
exec bash "$tool_dir/session-resume.sh" "$@"
|
||||
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-resume.sh" "$@"
|
||||
;;
|
||||
help|*)
|
||||
cat <<COORD_USAGE
|
||||
@@ -511,12 +545,23 @@ Commands:
|
||||
mission [--project <path>] Show mission progress dashboard
|
||||
status [--project <path>] Check agent session health
|
||||
continue [--project <path>] Generate continuation prompt for next session
|
||||
run [--project <path>] Generate context and launch selected runtime
|
||||
smoke Run orchestration behavior smoke checks
|
||||
resume [--project <path>] Crash recovery (detect dirty state, generate fix)
|
||||
|
||||
Runtime:
|
||||
--claude Use Claude runtime hints/prompts (default)
|
||||
--codex Use Codex runtime hints/prompts
|
||||
|
||||
Examples:
|
||||
mosaic coord init --name "Security Fix" --milestones "Critical,High,Medium"
|
||||
mosaic coord mission
|
||||
mosaic coord --codex mission
|
||||
mosaic coord continue --copy
|
||||
mosaic coord run
|
||||
mosaic coord run --codex
|
||||
mosaic coord smoke
|
||||
mosaic coord continue --codex --copy
|
||||
|
||||
COORD_USAGE
|
||||
;;
|
||||
@@ -552,33 +597,65 @@ _check_resumable_session() {
|
||||
|
||||
run_prdy() {
|
||||
check_mosaic_home
|
||||
local subcmd="${1:-help}"
|
||||
shift || true
|
||||
local runtime="claude"
|
||||
local runtime_flag=""
|
||||
local -a prdy_args=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--claude|--codex)
|
||||
local selected_runtime="${1#--}"
|
||||
if [[ -n "$runtime_flag" ]] && [[ "$runtime" != "$selected_runtime" ]]; then
|
||||
echo "[mosaic] ERROR: --claude and --codex are mutually exclusive for 'mosaic prdy'." >&2
|
||||
exit 1
|
||||
fi
|
||||
runtime="$selected_runtime"
|
||||
runtime_flag="$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
prdy_args+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local subcmd="${prdy_args[0]:-help}"
|
||||
if (( ${#prdy_args[@]} > 1 )); then
|
||||
set -- "${prdy_args[@]:1}"
|
||||
else
|
||||
set --
|
||||
fi
|
||||
|
||||
local tool_dir="$MOSAIC_HOME/tools/prdy"
|
||||
|
||||
case "$subcmd" in
|
||||
init)
|
||||
exec bash "$tool_dir/prdy-init.sh" "$@"
|
||||
MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-init.sh" "$@"
|
||||
;;
|
||||
update)
|
||||
exec bash "$tool_dir/prdy-update.sh" "$@"
|
||||
MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-update.sh" "$@"
|
||||
;;
|
||||
validate|check)
|
||||
exec bash "$tool_dir/prdy-validate.sh" "$@"
|
||||
MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-validate.sh" "$@"
|
||||
;;
|
||||
help|*)
|
||||
cat <<PRDY_USAGE
|
||||
mosaic prdy — PRD creation and validation tools
|
||||
|
||||
Commands:
|
||||
init [--project <path>] [--name <feature>] Create docs/PRD.md via guided Claude session
|
||||
update [--project <path>] Update existing docs/PRD.md via Claude session
|
||||
init [--project <path>] [--name <feature>] Create docs/PRD.md via guided runtime session
|
||||
update [--project <path>] Update existing docs/PRD.md via guided runtime session
|
||||
validate [--project <path>] Check PRD completeness against Mosaic guide (bash-only)
|
||||
|
||||
Runtime:
|
||||
--claude Use Claude runtime (default)
|
||||
--codex Use Codex runtime
|
||||
|
||||
Examples:
|
||||
mosaic prdy init --name "User Authentication"
|
||||
mosaic prdy update
|
||||
mosaic prdy --codex init --name "User Authentication"
|
||||
mosaic prdy validate
|
||||
|
||||
Output location: docs/PRD.md (per Mosaic PRD guide)
|
||||
|
||||
@@ -131,9 +131,9 @@ If context usage is high, produce a handoff message:
|
||||
4. Commit all state files
|
||||
5. The coordinator will generate a continuation prompt for the next session
|
||||
|
||||
### Continuation Prompt Format
|
||||
### Continuation Prompt and Capsule Format
|
||||
|
||||
The coordinator generates this (via `mosaic coord continue`):
|
||||
The coordinator generates this (via `mosaic coord continue`) and writes a machine-readable capsule at `.mosaic/orchestrator/next-task.json`:
|
||||
|
||||
```
|
||||
## Continuation Mission
|
||||
@@ -152,6 +152,16 @@ Continue **{mission}** from existing state.
|
||||
4. Human launches new session and pastes the prompt
|
||||
5. New agent reads manifest, scratchpad, TASKS.md and continues
|
||||
|
||||
### Between Sessions (r0 assisted)
|
||||
|
||||
Use `mosaic coord run` to remove copy/paste steps:
|
||||
|
||||
1. Agent stops
|
||||
2. Human runs `mosaic coord run [--claude|--codex]`
|
||||
3. Coordinator regenerates continuation prompt + `next-task.json`
|
||||
4. Coordinator launches selected runtime with scoped kickoff context
|
||||
5. New session resumes from next task
|
||||
|
||||
---
|
||||
|
||||
## 7. Failure Taxonomy Quick Reference
|
||||
@@ -194,6 +204,7 @@ In r0, the Coordinator is Jason + shell scripts. No daemon. No automation.
|
||||
| `mosaic coord mission` | Show mission progress dashboard |
|
||||
| `mosaic coord status` | Check if agent session is still running |
|
||||
| `mosaic coord continue` | Generate continuation prompt for next session |
|
||||
| `mosaic coord run [--claude|--codex]` | Generate continuation context and launch runtime |
|
||||
| `mosaic coord resume` | Crash recovery (detect dirty state, generate fix) |
|
||||
| `mosaic coord resume --clean-lock` | Clear stale session lock after review |
|
||||
|
||||
@@ -201,7 +212,7 @@ In r0, the Coordinator is Jason + shell scripts. No daemon. No automation.
|
||||
|
||||
```
|
||||
init → launch agent → [agent works] → agent stops →
|
||||
status → mission → continue → launch agent → repeat
|
||||
status → mission → run → repeat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -16,6 +16,21 @@ This file applies only to Codex runtime behavior.
|
||||
8. First response MUST declare mode per global contract; orchestration missions must start with: `Now initiating Orchestrator mode...`
|
||||
9. Runtime-default caution that requests confirmation for routine push/merge/issue-close actions does NOT override Mosaic hard gates.
|
||||
|
||||
## Strict Orchestrator Profile (Codex)
|
||||
|
||||
For orchestration missions, prefer `mosaic coord run --codex` over manual launch/paste.
|
||||
|
||||
When launched through coordinator run flow, Codex MUST:
|
||||
|
||||
1. Treat `.mosaic/orchestrator/next-task.json` as authoritative execution capsule.
|
||||
2. Read mission files before asking clarifying questions:
|
||||
- `~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md`
|
||||
- `docs/MISSION-MANIFEST.md`
|
||||
- `docs/scratchpads/<mission-id>.md`
|
||||
- `docs/TASKS.md`
|
||||
3. Avoid pre-execution question loops. Questions are allowed only for Mosaic escalation triggers (missing access/credentials, destructive irreversible action, legal/compliance unknowns, conflicting objectives, hard budget cap).
|
||||
4. Start execution on the `next_task` from capsule as soon as required files are loaded.
|
||||
|
||||
## Memory Override
|
||||
|
||||
Do NOT write durable memory to `~/.codex/` or any Codex-native session memory. All durable memory MUST be written to `~/.config/mosaic/memory/` per `~/.config/mosaic/guides/MEMORY.md`. Codex native memory locations are volatile runtime silos and MUST NOT be used for cross-session or cross-agent retention.
|
||||
|
||||
@@ -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:-.}"
|
||||
|
||||
@@ -30,6 +30,8 @@ done
|
||||
|
||||
_require_jq
|
||||
require_mission "$PROJECT"
|
||||
target_runtime="$(coord_runtime)"
|
||||
launch_cmd="$(coord_launch_command)"
|
||||
|
||||
# ─── Load mission data ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -93,6 +95,22 @@ if (( session_count > 0 )); then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Write machine-readable next-task capsule for deterministic runtime launches.
|
||||
write_next_task_capsule \
|
||||
"$PROJECT" \
|
||||
"$target_runtime" \
|
||||
"$mission_id" \
|
||||
"$mission_name" \
|
||||
"$project_path" \
|
||||
"$quality_gates" \
|
||||
"$current_ms_id" \
|
||||
"$current_ms_name" \
|
||||
"$next_task" \
|
||||
"$tasks_done" \
|
||||
"$tasks_total" \
|
||||
"$pct" \
|
||||
"$current_branch"
|
||||
|
||||
# ─── Generate prompt ────────────────────────────────────────────────────────
|
||||
|
||||
prompt="$(cat <<EOF
|
||||
@@ -108,6 +126,7 @@ Continue **$mission_name** from existing state.
|
||||
- **Scratchpad:** docs/scratchpads/${mission_id}.md
|
||||
- **Protocol:** ~/.config/mosaic/guides/ORCHESTRATOR.md
|
||||
- **Quality gates:** $quality_gates
|
||||
- **Target runtime:** $target_runtime
|
||||
|
||||
## Resume Point
|
||||
|
||||
@@ -129,9 +148,10 @@ Continue **$mission_name** from existing state.
|
||||
3. Read \`docs/scratchpads/${mission_id}.md\` for session history and decisions
|
||||
4. Read \`docs/TASKS.md\` for current task state
|
||||
5. \`git pull --rebase\` to sync latest changes
|
||||
6. Continue execution from task **${next_task:-next-pending}**
|
||||
7. Follow Two-Phase Completion Protocol
|
||||
8. You are the SOLE writer of \`docs/TASKS.md\`
|
||||
6. Launch runtime with \`$launch_cmd\`
|
||||
7. Continue execution from task **${next_task:-next-pending}**
|
||||
8. Follow Two-Phase Completion Protocol
|
||||
9. You are the SOLE writer of \`docs/TASKS.md\`
|
||||
EOF
|
||||
)"
|
||||
|
||||
|
||||
@@ -270,6 +270,9 @@ fi
|
||||
|
||||
# ─── Report ──────────────────────────────────────────────────────────────────
|
||||
|
||||
runtime_cmd="$(coord_launch_command)"
|
||||
run_cmd="$(coord_run_command)"
|
||||
|
||||
echo ""
|
||||
echo -e "${C_GREEN}${C_BOLD}Mission initialized: $NAME${C_RESET}"
|
||||
echo ""
|
||||
@@ -280,4 +283,4 @@ echo -e " ${C_CYAN}Manifest:${C_RESET} $manifest_path"
|
||||
echo -e " ${C_CYAN}Scratchpad:${C_RESET} $sp_file"
|
||||
echo -e " ${C_CYAN}Tasks:${C_RESET} $tasks_path"
|
||||
echo ""
|
||||
echo "Next: Launch an agent session with 'mosaic claude' or generate a prompt with 'mosaic coord continue'"
|
||||
echo "Next: Resume with '$run_cmd' (or launch directly with '$runtime_cmd')."
|
||||
|
||||
80
tools/orchestrator/session-run.sh
Executable file
80
tools/orchestrator/session-run.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
#
|
||||
# session-run.sh — Generate continuation context and launch target runtime.
|
||||
#
|
||||
# Usage:
|
||||
# session-run.sh [--project <path>] [--milestone <id>] [--print]
|
||||
#
|
||||
# Behavior:
|
||||
# - Builds continuation prompt + next-task capsule.
|
||||
# - Launches selected runtime (default: claude, override via MOSAIC_COORD_RUNTIME).
|
||||
# - For codex, injects strict orchestration kickoff to reduce clarification loops.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/_lib.sh"
|
||||
|
||||
PROJECT="."
|
||||
MILESTONE=""
|
||||
PRINT=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--project) PROJECT="$2"; shift 2 ;;
|
||||
--milestone) MILESTONE="$2"; shift 2 ;;
|
||||
--print) PRINT=true; shift ;;
|
||||
-h|--help)
|
||||
cat <<'USAGE'
|
||||
Usage: session-run.sh [--project <path>] [--milestone <id>] [--print]
|
||||
|
||||
Options:
|
||||
--project <path> Project directory (default: CWD)
|
||||
--milestone <id> Force specific milestone context
|
||||
--print Print launch prompt only (no runtime launch)
|
||||
USAGE
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
PROJECT="${PROJECT/#\~/$HOME}"
|
||||
PROJECT="$(cd "$PROJECT" && pwd)"
|
||||
|
||||
_require_jq
|
||||
require_mission "$PROJECT"
|
||||
|
||||
runtime="$(coord_runtime)"
|
||||
launch_cmd="$(coord_launch_command)"
|
||||
|
||||
continue_cmd=(bash "$SCRIPT_DIR/continue-prompt.sh" --project "$PROJECT")
|
||||
if [[ -n "$MILESTONE" ]]; then
|
||||
continue_cmd+=(--milestone "$MILESTONE")
|
||||
fi
|
||||
|
||||
continuation_prompt="$(MOSAIC_COORD_RUNTIME="$runtime" "${continue_cmd[@]}")"
|
||||
|
||||
if [[ "$runtime" == "codex" ]]; then
|
||||
launch_prompt="$(build_codex_strict_kickoff "$PROJECT" "$continuation_prompt")"
|
||||
else
|
||||
launch_prompt="$continuation_prompt"
|
||||
fi
|
||||
|
||||
if [[ "$PRINT" == true ]]; then
|
||||
echo "$launch_prompt"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${C_CYAN}Launching orchestration runtime: ${launch_cmd}${C_RESET}"
|
||||
echo -e "${C_CYAN}Project:${C_RESET} $PROJECT"
|
||||
echo -e "${C_CYAN}Capsule:${C_RESET} $(next_task_capsule_path "$PROJECT")"
|
||||
|
||||
cd "$PROJECT"
|
||||
if [[ "$runtime" == "claude" ]]; then
|
||||
exec "$MOSAIC_HOME/bin/mosaic" claude "$launch_prompt"
|
||||
elif [[ "$runtime" == "codex" ]]; then
|
||||
exec "$MOSAIC_HOME/bin/mosaic" codex "$launch_prompt"
|
||||
fi
|
||||
|
||||
echo -e "${C_RED}Unsupported coord runtime: $runtime${C_RESET}" >&2
|
||||
exit 1
|
||||
@@ -33,6 +33,8 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
|
||||
_require_jq
|
||||
runtime_cmd="$(coord_launch_command)"
|
||||
run_cmd="$(coord_run_command)"
|
||||
|
||||
# ─── Check session lock ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -103,8 +105,9 @@ if ! lock_data="$(session_lock_read "$PROJECT")"; then
|
||||
|
||||
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
|
||||
echo -e " ${C_BOLD}Next steps:${C_RESET}"
|
||||
echo " $run_cmd Auto-generate context and launch"
|
||||
echo " mosaic coord continue Generate continuation prompt"
|
||||
echo " mosaic yolo claude Launch agent session"
|
||||
echo " $runtime_cmd Launch agent session"
|
||||
elif [[ "$m_status" == "completed" ]]; then
|
||||
echo -e " ${C_DIM}Mission completed. Start a new one with: mosaic coord init${C_RESET}"
|
||||
else
|
||||
|
||||
78
tools/orchestrator/smoke-test.sh
Executable file
78
tools/orchestrator/smoke-test.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
#
|
||||
# smoke-test.sh — Behavior smoke checks for coord continue/run workflows.
|
||||
#
|
||||
# Usage:
|
||||
# smoke-test.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/_lib.sh"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
pass_case() {
|
||||
echo "PASS: $1"
|
||||
PASS=$((PASS + 1))
|
||||
}
|
||||
|
||||
fail_case() {
|
||||
echo "FAIL: $1" >&2
|
||||
FAIL=$((FAIL + 1))
|
||||
}
|
||||
|
||||
tmp_project="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_project"' EXIT
|
||||
|
||||
mkdir -p "$tmp_project/.mosaic/orchestrator" "$tmp_project/docs/scratchpads"
|
||||
|
||||
cat > "$tmp_project/.mosaic/orchestrator/mission.json" <<'JSON'
|
||||
{
|
||||
"mission_id": "smoke-mission-20260223",
|
||||
"name": "Smoke Mission",
|
||||
"status": "active",
|
||||
"project_path": "SMOKE_PROJECT",
|
||||
"quality_gates": "pnpm lint && pnpm test",
|
||||
"milestones": [
|
||||
{ "id": "M1", "name": "Milestone One", "status": "pending" }
|
||||
],
|
||||
"sessions": []
|
||||
}
|
||||
JSON
|
||||
|
||||
cat > "$tmp_project/docs/MISSION-MANIFEST.md" <<'MD'
|
||||
# Mission Manifest
|
||||
MD
|
||||
|
||||
cat > "$tmp_project/docs/scratchpads/smoke-mission-20260223.md" <<'MD'
|
||||
# Scratchpad
|
||||
MD
|
||||
|
||||
cat > "$tmp_project/docs/TASKS.md" <<'MD'
|
||||
| id | status | milestone | description | pr | notes |
|
||||
|----|--------|-----------|-------------|----|-------|
|
||||
| T-001 | pending | M1 | Smoke task | | |
|
||||
MD
|
||||
|
||||
codex_continue_output="$(MOSAIC_COORD_RUNTIME=codex bash "$SCRIPT_DIR/continue-prompt.sh" --project "$tmp_project")"
|
||||
capsule_file="$tmp_project/.mosaic/orchestrator/next-task.json"
|
||||
|
||||
if [[ -f "$capsule_file" ]]; then pass_case "continue writes next-task capsule"; else fail_case "continue writes next-task capsule"; fi
|
||||
if jq -e '.runtime == "codex"' "$capsule_file" >/dev/null 2>&1; then pass_case "capsule runtime is codex"; else fail_case "capsule runtime is codex"; fi
|
||||
if jq -e '.next_task == "T-001"' "$capsule_file" >/dev/null 2>&1; then pass_case "capsule next_task is T-001"; else fail_case "capsule next_task is T-001"; fi
|
||||
if grep -Fq 'Target runtime:** codex' <<< "$codex_continue_output"; then pass_case "continue prompt contains target runtime codex"; else fail_case "continue prompt contains target runtime codex"; fi
|
||||
|
||||
codex_run_prompt="$(MOSAIC_COORD_RUNTIME=codex bash "$SCRIPT_DIR/session-run.sh" --project "$tmp_project" --print)"
|
||||
if [[ "$(printf '%s\n' "$codex_run_prompt" | head -n1)" == "Now initiating Orchestrator mode..." ]]; then pass_case "codex run prompt first line is mode declaration"; else fail_case "codex run prompt first line is mode declaration"; fi
|
||||
if grep -Fq 'Do NOT ask clarifying questions before your first tool actions' <<< "$codex_run_prompt"; then pass_case "codex run prompt includes no-questions hard gate"; else fail_case "codex run prompt includes no-questions hard gate"; fi
|
||||
if grep -Fq '"next_task": "T-001"' <<< "$codex_run_prompt"; then pass_case "codex run prompt embeds capsule json"; else fail_case "codex run prompt embeds capsule json"; fi
|
||||
|
||||
claude_run_prompt="$(MOSAIC_COORD_RUNTIME=claude bash "$SCRIPT_DIR/session-run.sh" --project "$tmp_project" --print)"
|
||||
if [[ "$(printf '%s\n' "$claude_run_prompt" | head -n1)" == "## Continuation Mission" ]]; then pass_case "claude run prompt remains continuation prompt format"; else fail_case "claude run prompt remains continuation prompt format"; fi
|
||||
|
||||
echo ""
|
||||
echo "Smoke test summary: pass=$PASS fail=$FAIL"
|
||||
if (( FAIL > 0 )); then
|
||||
exit 1
|
||||
fi
|
||||
@@ -41,6 +41,20 @@ _require_cmd() {
|
||||
fi
|
||||
}
|
||||
|
||||
prdy_runtime() {
|
||||
local runtime="${MOSAIC_PRDY_RUNTIME:-claude}"
|
||||
case "$runtime" in
|
||||
claude|codex) echo "$runtime" ;;
|
||||
*) echo "claude" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
prdy_runtime_command() {
|
||||
local runtime
|
||||
runtime="$(prdy_runtime)"
|
||||
echo "$runtime"
|
||||
}
|
||||
|
||||
# ─── PRD detection ───────────────────────────────────────────────────────────
|
||||
|
||||
# Find the PRD file in a project directory.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
#
|
||||
# prdy-init.sh — Create a new PRD via guided Claude session
|
||||
# prdy-init.sh — Create a new PRD via guided runtime session
|
||||
#
|
||||
# Usage:
|
||||
# prdy-init.sh [--project <path>] [--name <feature>]
|
||||
#
|
||||
# Launches a dedicated Claude Code session in yolo mode with a specialized
|
||||
# Launches a dedicated runtime session in yolo mode with a specialized
|
||||
# system prompt that guides the user through PRD creation. The output is
|
||||
# written to docs/PRD.md.
|
||||
|
||||
@@ -24,15 +24,15 @@ while [[ $# -gt 0 ]]; do
|
||||
--name) NAME="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
cat <<'USAGE'
|
||||
prdy-init.sh — Create a new PRD via guided Claude session
|
||||
prdy-init.sh — Create a new PRD via guided runtime session
|
||||
|
||||
Usage: prdy-init.sh [--project <path>] [--name <feature>]
|
||||
|
||||
Options:
|
||||
--project <path> Project directory (default: CWD)
|
||||
--name <feature> Feature or project name (optional, Claude will ask if omitted)
|
||||
--name <feature> Feature or project name (optional, runtime will ask if omitted)
|
||||
|
||||
Launches Claude Code in yolo mode with a PRD-focused system prompt.
|
||||
Launches the selected runtime in yolo mode with a PRD-focused prompt.
|
||||
The agent will ask clarifying questions, then write docs/PRD.md.
|
||||
|
||||
Examples:
|
||||
@@ -51,7 +51,8 @@ PROJECT="${PROJECT/#\~/$HOME}"
|
||||
|
||||
# ─── Preflight checks ───────────────────────────────────────────────────────
|
||||
|
||||
_require_cmd "claude"
|
||||
RUNTIME_CMD="$(prdy_runtime_command)"
|
||||
_require_cmd "$RUNTIME_CMD"
|
||||
|
||||
# Check for existing PRD
|
||||
EXISTING="$(find_prd "$PROJECT")"
|
||||
@@ -77,11 +78,29 @@ else
|
||||
KICKOFF="Create docs/PRD.md for this project. Read the project context first, then ask the user what they want to build. Ask clarifying questions before writing the PRD."
|
||||
fi
|
||||
|
||||
# ─── Launch Claude ───────────────────────────────────────────────────────────
|
||||
# ─── Launch runtime ──────────────────────────────────────────────────────────
|
||||
|
||||
info "Output target: $PROJECT/$PRD_CANONICAL"
|
||||
info "Mode: PRD Creation (yolo)"
|
||||
info "Mode: PRD Creation (yolo, runtime: $RUNTIME_CMD)"
|
||||
echo ""
|
||||
|
||||
cd "$PROJECT"
|
||||
exec claude --dangerously-skip-permissions --append-system-prompt "$SYSTEM_PROMPT" "$KICKOFF"
|
||||
if [[ "$RUNTIME_CMD" == "claude" ]]; then
|
||||
exec claude --dangerously-skip-permissions --append-system-prompt "$SYSTEM_PROMPT" "$KICKOFF"
|
||||
fi
|
||||
|
||||
if [[ "$RUNTIME_CMD" == "codex" ]]; then
|
||||
CODEX_PROMPT="$(cat <<EOF
|
||||
Follow this PRD contract exactly.
|
||||
|
||||
$SYSTEM_PROMPT
|
||||
|
||||
Task:
|
||||
$KICKOFF
|
||||
EOF
|
||||
)"
|
||||
exec codex --dangerously-bypass-approvals-and-sandbox "$CODEX_PROMPT"
|
||||
fi
|
||||
|
||||
fail "Unsupported runtime: $RUNTIME_CMD"
|
||||
exit 1
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
#
|
||||
# prdy-update.sh — Update an existing PRD via guided Claude session
|
||||
# prdy-update.sh — Update an existing PRD via guided runtime session
|
||||
#
|
||||
# Usage:
|
||||
# prdy-update.sh [--project <path>]
|
||||
#
|
||||
# Launches a dedicated Claude Code session in yolo mode with a specialized
|
||||
# Launches a dedicated runtime session in yolo mode with a specialized
|
||||
# system prompt that reads the existing PRD and guides targeted modifications.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -21,14 +21,14 @@ while [[ $# -gt 0 ]]; do
|
||||
--project) PROJECT="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
cat <<'USAGE'
|
||||
prdy-update.sh — Update an existing PRD via guided Claude session
|
||||
prdy-update.sh — Update an existing PRD via guided runtime session
|
||||
|
||||
Usage: prdy-update.sh [--project <path>]
|
||||
|
||||
Options:
|
||||
--project <path> Project directory (default: CWD)
|
||||
|
||||
Launches Claude Code in yolo mode with a PRD-update system prompt.
|
||||
Launches the selected runtime in yolo mode with a PRD-update prompt.
|
||||
The agent will read the existing docs/PRD.md, summarize its state,
|
||||
and ask what changes are needed.
|
||||
|
||||
@@ -47,7 +47,8 @@ PROJECT="${PROJECT/#\~/$HOME}"
|
||||
|
||||
# ─── Preflight checks ───────────────────────────────────────────────────────
|
||||
|
||||
_require_cmd "claude"
|
||||
RUNTIME_CMD="$(prdy_runtime_command)"
|
||||
_require_cmd "$RUNTIME_CMD"
|
||||
|
||||
# Require existing PRD
|
||||
EXISTING="$(find_prd "$PROJECT")"
|
||||
@@ -65,11 +66,29 @@ SYSTEM_PROMPT="$(build_prdy_system_prompt "update")"
|
||||
|
||||
KICKOFF="Read the existing PRD at ${EXISTING}, summarize its current state, then ask what changes or additions are needed."
|
||||
|
||||
# ─── Launch Claude ───────────────────────────────────────────────────────────
|
||||
# ─── Launch runtime ──────────────────────────────────────────────────────────
|
||||
|
||||
info "Updating: $EXISTING"
|
||||
info "Mode: PRD Update (yolo)"
|
||||
info "Mode: PRD Update (yolo, runtime: $RUNTIME_CMD)"
|
||||
echo ""
|
||||
|
||||
cd "$PROJECT"
|
||||
exec claude --dangerously-skip-permissions --append-system-prompt "$SYSTEM_PROMPT" "$KICKOFF"
|
||||
if [[ "$RUNTIME_CMD" == "claude" ]]; then
|
||||
exec claude --dangerously-skip-permissions --append-system-prompt "$SYSTEM_PROMPT" "$KICKOFF"
|
||||
fi
|
||||
|
||||
if [[ "$RUNTIME_CMD" == "codex" ]]; then
|
||||
CODEX_PROMPT="$(cat <<EOF
|
||||
Follow this PRD contract exactly.
|
||||
|
||||
$SYSTEM_PROMPT
|
||||
|
||||
Task:
|
||||
$KICKOFF
|
||||
EOF
|
||||
)"
|
||||
exec codex --dangerously-bypass-approvals-and-sandbox "$CODEX_PROMPT"
|
||||
fi
|
||||
|
||||
fail "Unsupported runtime: $RUNTIME_CMD"
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user