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

@@ -53,8 +53,8 @@ Management:
PRD: PRD:
prdy <subcommand> PRD creation and validation prdy <subcommand> PRD creation and validation
init Create docs/PRD.md via Claude session init Create docs/PRD.md via guided runtime session
update Update existing PRD via Claude session update Update existing PRD via guided runtime session
validate Check PRD completeness (bash-only) validate Check PRD completeness (bash-only)
Coordinator (r0): Coordinator (r0):
@@ -63,6 +63,7 @@ Coordinator (r0):
mission Show mission progress dashboard mission Show mission progress dashboard
status Check agent session health status Check agent session health
continue Generate continuation prompt continue Generate continuation prompt
run Generate context and launch selected runtime
resume Crash recovery resume Crash recovery
Options: Options:
@@ -481,26 +482,59 @@ run_seq() {
run_coord() { run_coord() {
check_mosaic_home check_mosaic_home
local subcmd="${1:-help}" local runtime="claude"
shift || true 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" local tool_dir="$MOSAIC_HOME/tools/orchestrator"
case "$subcmd" in case "$subcmd" in
status|session) status|session)
exec bash "$tool_dir/session-status.sh" "$@" MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-status.sh" "$@"
;; ;;
init) init)
exec bash "$tool_dir/mission-init.sh" "$@" MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-init.sh" "$@"
;; ;;
mission|progress) mission|progress)
exec bash "$tool_dir/mission-status.sh" "$@" MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-status.sh" "$@"
;; ;;
continue|next) 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) resume|recover)
exec bash "$tool_dir/session-resume.sh" "$@" MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-resume.sh" "$@"
;; ;;
help|*) help|*)
cat <<COORD_USAGE cat <<COORD_USAGE
@@ -511,12 +545,23 @@ Commands:
mission [--project <path>] Show mission progress dashboard mission [--project <path>] Show mission progress dashboard
status [--project <path>] Check agent session health status [--project <path>] Check agent session health
continue [--project <path>] Generate continuation prompt for next session 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) 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: Examples:
mosaic coord init --name "Security Fix" --milestones "Critical,High,Medium" mosaic coord init --name "Security Fix" --milestones "Critical,High,Medium"
mosaic coord mission mosaic coord mission
mosaic coord --codex mission
mosaic coord continue --copy mosaic coord continue --copy
mosaic coord run
mosaic coord run --codex
mosaic coord smoke
mosaic coord continue --codex --copy
COORD_USAGE COORD_USAGE
;; ;;
@@ -552,33 +597,65 @@ _check_resumable_session() {
run_prdy() { run_prdy() {
check_mosaic_home check_mosaic_home
local subcmd="${1:-help}" local runtime="claude"
shift || true 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" local tool_dir="$MOSAIC_HOME/tools/prdy"
case "$subcmd" in case "$subcmd" in
init) init)
exec bash "$tool_dir/prdy-init.sh" "$@" MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-init.sh" "$@"
;; ;;
update) update)
exec bash "$tool_dir/prdy-update.sh" "$@" MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-update.sh" "$@"
;; ;;
validate|check) validate|check)
exec bash "$tool_dir/prdy-validate.sh" "$@" MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-validate.sh" "$@"
;; ;;
help|*) help|*)
cat <<PRDY_USAGE cat <<PRDY_USAGE
mosaic prdy — PRD creation and validation tools mosaic prdy — PRD creation and validation tools
Commands: Commands:
init [--project <path>] [--name <feature>] Create docs/PRD.md via guided Claude session init [--project <path>] [--name <feature>] Create docs/PRD.md via guided runtime session
update [--project <path>] Update existing docs/PRD.md via Claude session update [--project <path>] Update existing docs/PRD.md via guided runtime session
validate [--project <path>] Check PRD completeness against Mosaic guide (bash-only) validate [--project <path>] Check PRD completeness against Mosaic guide (bash-only)
Runtime:
--claude Use Claude runtime (default)
--codex Use Codex runtime
Examples: Examples:
mosaic prdy init --name "User Authentication" mosaic prdy init --name "User Authentication"
mosaic prdy update mosaic prdy update
mosaic prdy --codex init --name "User Authentication"
mosaic prdy validate mosaic prdy validate
Output location: docs/PRD.md (per Mosaic PRD guide) Output location: docs/PRD.md (per Mosaic PRD guide)

View File

@@ -131,9 +131,9 @@ If context usage is high, produce a handoff message:
4. Commit all state files 4. Commit all state files
5. The coordinator will generate a continuation prompt for the next session 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 ## Continuation Mission
@@ -152,6 +152,16 @@ Continue **{mission}** from existing state.
4. Human launches new session and pastes the prompt 4. Human launches new session and pastes the prompt
5. New agent reads manifest, scratchpad, TASKS.md and continues 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 ## 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 mission` | Show mission progress dashboard |
| `mosaic coord status` | Check if agent session is still running | | `mosaic coord status` | Check if agent session is still running |
| `mosaic coord continue` | Generate continuation prompt for next session | | `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` | Crash recovery (detect dirty state, generate fix) |
| `mosaic coord resume --clean-lock` | Clear stale session lock after review | | `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 → init → launch agent → [agent works] → agent stops →
status → mission → continue → launch agent → repeat status → mission → run → repeat
``` ```
--- ---

View File

@@ -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...` 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. 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 ## 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. 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.

View File

@@ -11,6 +11,7 @@ MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
ORCH_SUBDIR=".mosaic/orchestrator" ORCH_SUBDIR=".mosaic/orchestrator"
MISSION_FILE="mission.json" MISSION_FILE="mission.json"
SESSION_LOCK_FILE="session.lock" SESSION_LOCK_FILE="session.lock"
NEXT_TASK_FILE="next-task.json"
MANIFEST_FILE="docs/MISSION-MANIFEST.md" MANIFEST_FILE="docs/MISSION-MANIFEST.md"
TASKS_MD="docs/TASKS.md" TASKS_MD="docs/TASKS.md"
SCRATCHPAD_DIR="docs/scratchpads" SCRATCHPAD_DIR="docs/scratchpads"
@@ -42,6 +43,30 @@ _require_jq() {
fi 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 ──────────────────────────────────────────── # ─── Project / state file access ────────────────────────────────────────────
# Return the orchestrator directory for a project # Return the orchestrator directory for a project
@@ -56,6 +81,11 @@ mission_path() {
echo "$(orch_dir "$project")/$MISSION_FILE" 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 # Exit with error if mission.json is missing or inactive
require_mission() { require_mission() {
local project="${1:-.}" local project="${1:-.}"
@@ -358,6 +388,113 @@ milestone_name() {
jq -r --arg id "$mid" '.milestones[] | select(.id == $id) | .name // empty' "$mp" 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 # Get next milestone after the given one
next_milestone_id() { next_milestone_id() {
local project="${1:-.}" local project="${1:-.}"

View File

@@ -30,6 +30,8 @@ done
_require_jq _require_jq
require_mission "$PROJECT" require_mission "$PROJECT"
target_runtime="$(coord_runtime)"
launch_cmd="$(coord_launch_command)"
# ─── Load mission data ────────────────────────────────────────────────────── # ─── Load mission data ──────────────────────────────────────────────────────
@@ -93,6 +95,22 @@ if (( session_count > 0 )); then
fi fi
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 ──────────────────────────────────────────────────────── # ─── Generate prompt ────────────────────────────────────────────────────────
prompt="$(cat <<EOF prompt="$(cat <<EOF
@@ -108,6 +126,7 @@ Continue **$mission_name** from existing state.
- **Scratchpad:** docs/scratchpads/${mission_id}.md - **Scratchpad:** docs/scratchpads/${mission_id}.md
- **Protocol:** ~/.config/mosaic/guides/ORCHESTRATOR.md - **Protocol:** ~/.config/mosaic/guides/ORCHESTRATOR.md
- **Quality gates:** $quality_gates - **Quality gates:** $quality_gates
- **Target runtime:** $target_runtime
## Resume Point ## Resume Point
@@ -129,9 +148,10 @@ Continue **$mission_name** from existing state.
3. Read \`docs/scratchpads/${mission_id}.md\` for session history and decisions 3. Read \`docs/scratchpads/${mission_id}.md\` for session history and decisions
4. Read \`docs/TASKS.md\` for current task state 4. Read \`docs/TASKS.md\` for current task state
5. \`git pull --rebase\` to sync latest changes 5. \`git pull --rebase\` to sync latest changes
6. Continue execution from task **${next_task:-next-pending}** 6. Launch runtime with \`$launch_cmd\`
7. Follow Two-Phase Completion Protocol 7. Continue execution from task **${next_task:-next-pending}**
8. You are the SOLE writer of \`docs/TASKS.md\` 8. Follow Two-Phase Completion Protocol
9. You are the SOLE writer of \`docs/TASKS.md\`
EOF EOF
)" )"

View File

@@ -270,6 +270,9 @@ fi
# ─── Report ────────────────────────────────────────────────────────────────── # ─── Report ──────────────────────────────────────────────────────────────────
runtime_cmd="$(coord_launch_command)"
run_cmd="$(coord_run_command)"
echo "" echo ""
echo -e "${C_GREEN}${C_BOLD}Mission initialized: $NAME${C_RESET}" echo -e "${C_GREEN}${C_BOLD}Mission initialized: $NAME${C_RESET}"
echo "" 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}Scratchpad:${C_RESET} $sp_file"
echo -e " ${C_CYAN}Tasks:${C_RESET} $tasks_path" echo -e " ${C_CYAN}Tasks:${C_RESET} $tasks_path"
echo "" 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')."

View 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

View File

@@ -33,6 +33,8 @@ while [[ $# -gt 0 ]]; do
done done
_require_jq _require_jq
runtime_cmd="$(coord_launch_command)"
run_cmd="$(coord_run_command)"
# ─── Check session lock ───────────────────────────────────────────────────── # ─── Check session lock ─────────────────────────────────────────────────────
@@ -103,8 +105,9 @@ if ! lock_data="$(session_lock_read "$PROJECT")"; then
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
echo -e " ${C_BOLD}Next steps:${C_RESET}" 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 coord continue Generate continuation prompt"
echo " mosaic yolo claude Launch agent session" echo " $runtime_cmd Launch agent session"
elif [[ "$m_status" == "completed" ]]; then elif [[ "$m_status" == "completed" ]]; then
echo -e " ${C_DIM}Mission completed. Start a new one with: mosaic coord init${C_RESET}" echo -e " ${C_DIM}Mission completed. Start a new one with: mosaic coord init${C_RESET}"
else else

View 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

View File

@@ -41,6 +41,20 @@ _require_cmd() {
fi 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 ─────────────────────────────────────────────────────────── # ─── PRD detection ───────────────────────────────────────────────────────────
# Find the PRD file in a project directory. # Find the PRD file in a project directory.

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail 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: # Usage:
# prdy-init.sh [--project <path>] [--name <feature>] # 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 # system prompt that guides the user through PRD creation. The output is
# written to docs/PRD.md. # written to docs/PRD.md.
@@ -24,15 +24,15 @@ while [[ $# -gt 0 ]]; do
--name) NAME="$2"; shift 2 ;; --name) NAME="$2"; shift 2 ;;
-h|--help) -h|--help)
cat <<'USAGE' 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>] Usage: prdy-init.sh [--project <path>] [--name <feature>]
Options: Options:
--project <path> Project directory (default: CWD) --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. The agent will ask clarifying questions, then write docs/PRD.md.
Examples: Examples:
@@ -51,7 +51,8 @@ PROJECT="${PROJECT/#\~/$HOME}"
# ─── Preflight checks ─────────────────────────────────────────────────────── # ─── Preflight checks ───────────────────────────────────────────────────────
_require_cmd "claude" RUNTIME_CMD="$(prdy_runtime_command)"
_require_cmd "$RUNTIME_CMD"
# Check for existing PRD # Check for existing PRD
EXISTING="$(find_prd "$PROJECT")" 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." 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 fi
# ─── Launch Claude ────────────────────────────────────────────────────────── # ─── Launch runtime ──────────────────────────────────────────────────────────
info "Output target: $PROJECT/$PRD_CANONICAL" info "Output target: $PROJECT/$PRD_CANONICAL"
info "Mode: PRD Creation (yolo)" info "Mode: PRD Creation (yolo, runtime: $RUNTIME_CMD)"
echo "" echo ""
cd "$PROJECT" 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

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail 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: # Usage:
# prdy-update.sh [--project <path>] # 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. # system prompt that reads the existing PRD and guides targeted modifications.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -21,14 +21,14 @@ while [[ $# -gt 0 ]]; do
--project) PROJECT="$2"; shift 2 ;; --project) PROJECT="$2"; shift 2 ;;
-h|--help) -h|--help)
cat <<'USAGE' 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>] Usage: prdy-update.sh [--project <path>]
Options: Options:
--project <path> Project directory (default: CWD) --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, The agent will read the existing docs/PRD.md, summarize its state,
and ask what changes are needed. and ask what changes are needed.
@@ -47,7 +47,8 @@ PROJECT="${PROJECT/#\~/$HOME}"
# ─── Preflight checks ─────────────────────────────────────────────────────── # ─── Preflight checks ───────────────────────────────────────────────────────
_require_cmd "claude" RUNTIME_CMD="$(prdy_runtime_command)"
_require_cmd "$RUNTIME_CMD"
# Require existing PRD # Require existing PRD
EXISTING="$(find_prd "$PROJECT")" 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." 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 "Updating: $EXISTING"
info "Mode: PRD Update (yolo)" info "Mode: PRD Update (yolo, runtime: $RUNTIME_CMD)"
echo "" echo ""
cd "$PROJECT" 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