- 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>
287 lines
8.9 KiB
Bash
Executable File
287 lines
8.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
#
|
|
# mission-init.sh — Initialize a new orchestration mission
|
|
#
|
|
# Usage:
|
|
# mission-init.sh --name <name> [options]
|
|
#
|
|
# Options:
|
|
# --name <name> Mission name (required)
|
|
# --project <path> Project directory (default: CWD)
|
|
# --prefix <prefix> Task ID prefix (e.g., MS)
|
|
# --milestones <comma-list> Milestone names, comma-separated
|
|
# --quality-gates <command> Quality gate command string
|
|
# --version <semver> Milestone version (default: 0.0.1)
|
|
# --description <text> Mission description
|
|
# --force Overwrite existing active mission
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/_lib.sh"
|
|
|
|
# ─── Parse arguments ─────────────────────────────────────────────────────────
|
|
|
|
NAME=""
|
|
PROJECT="."
|
|
PREFIX=""
|
|
MILESTONES=""
|
|
QUALITY_GATES=""
|
|
VERSION="0.0.1"
|
|
DESCRIPTION=""
|
|
FORCE=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--name) NAME="$2"; shift 2 ;;
|
|
--project) PROJECT="$2"; shift 2 ;;
|
|
--prefix) PREFIX="$2"; shift 2 ;;
|
|
--milestones) MILESTONES="$2"; shift 2 ;;
|
|
--quality-gates) QUALITY_GATES="$2"; shift 2 ;;
|
|
--version) VERSION="$2"; shift 2 ;;
|
|
--description) DESCRIPTION="$2"; shift 2 ;;
|
|
--force) FORCE=true; shift ;;
|
|
-h|--help)
|
|
cat <<'USAGE'
|
|
mission-init.sh — Initialize a new orchestration mission
|
|
|
|
Usage: mission-init.sh --name <name> [options]
|
|
|
|
Options:
|
|
--name <name> Mission name (required)
|
|
--project <path> Project directory (default: CWD)
|
|
--prefix <prefix> Task ID prefix (e.g., MS)
|
|
--milestones <comma-list> Milestone names, comma-separated
|
|
--quality-gates <command> Quality gate command string
|
|
--version <semver> Milestone version (default: 0.0.1)
|
|
--description <text> Mission description
|
|
--force Overwrite existing active mission
|
|
|
|
Example:
|
|
mosaic coord init \
|
|
--name "Security Remediation" \
|
|
--prefix SEC \
|
|
--milestones "Critical Fixes,High Priority,Code Quality" \
|
|
--quality-gates "pnpm lint && pnpm typecheck && pnpm test"
|
|
USAGE
|
|
exit 0
|
|
;;
|
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$NAME" ]]; then
|
|
echo -e "${C_RED}Error: --name is required${C_RESET}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
_require_jq
|
|
|
|
# ─── Validate project ───────────────────────────────────────────────────────
|
|
|
|
od="$(orch_dir "$PROJECT")"
|
|
if [[ ! -d "$od" ]]; then
|
|
echo -e "${C_RED}Error: $od not found. Run 'mosaic bootstrap' first.${C_RESET}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Check for existing active mission
|
|
mp="$(mission_path "$PROJECT")"
|
|
if [[ -f "$mp" ]]; then
|
|
existing_status="$(jq -r '.status // "inactive"' "$mp")"
|
|
if [[ "$existing_status" == "active" || "$existing_status" == "paused" ]] && [[ "$FORCE" != true ]]; then
|
|
existing_name="$(jq -r '.name // "unnamed"' "$mp")"
|
|
echo -e "${C_YELLOW}Active mission exists: $existing_name (status: $existing_status)${C_RESET}" >&2
|
|
echo "Use --force to overwrite." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ─── Generate mission ID ────────────────────────────────────────────────────
|
|
|
|
MISSION_ID="$(slugify "$NAME")-$(date +%Y%m%d)"
|
|
|
|
# ─── Build milestones array ─────────────────────────────────────────────────
|
|
|
|
milestones_json="[]"
|
|
if [[ -n "$MILESTONES" ]]; then
|
|
IFS=',' read -ra ms_array <<< "$MILESTONES"
|
|
for i in "${!ms_array[@]}"; do
|
|
ms_name="$(echo "${ms_array[$i]}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
ms_id="phase-$(( i + 1 ))"
|
|
ms_branch="$(slugify "$ms_name")"
|
|
milestones_json="$(echo "$milestones_json" | jq \
|
|
--arg id "$ms_id" \
|
|
--arg name "$ms_name" \
|
|
--arg branch "$ms_branch" \
|
|
'. + [{
|
|
"id": $id,
|
|
"name": $name,
|
|
"status": "pending",
|
|
"branch": $branch,
|
|
"issue_ref": "",
|
|
"started_at": "",
|
|
"completed_at": ""
|
|
}]')"
|
|
done
|
|
fi
|
|
|
|
MILESTONE_COUNT="$(echo "$milestones_json" | jq 'length')"
|
|
|
|
# ─── Write mission.json ─────────────────────────────────────────────────────
|
|
|
|
mission_json="$(jq -n \
|
|
--arg mid "$MISSION_ID" \
|
|
--arg name "$NAME" \
|
|
--arg desc "$DESCRIPTION" \
|
|
--arg pp "$(cd "$PROJECT" && pwd)" \
|
|
--arg ts "$(iso_now)" \
|
|
--arg prefix "$PREFIX" \
|
|
--arg qg "$QUALITY_GATES" \
|
|
--arg ver "$VERSION" \
|
|
--argjson milestones "$milestones_json" \
|
|
'{
|
|
schema_version: 1,
|
|
mission_id: $mid,
|
|
name: $name,
|
|
description: $desc,
|
|
project_path: $pp,
|
|
created_at: $ts,
|
|
status: "active",
|
|
task_prefix: $prefix,
|
|
quality_gates: $qg,
|
|
milestone_version: $ver,
|
|
milestones: $milestones,
|
|
sessions: []
|
|
}')"
|
|
|
|
write_json "$mp" "$mission_json"
|
|
|
|
# ─── Scaffold MISSION-MANIFEST.md ───────────────────────────────────────────
|
|
|
|
manifest_path="$PROJECT/$MANIFEST_FILE"
|
|
mkdir -p "$(dirname "$manifest_path")"
|
|
|
|
if [[ ! -f "$manifest_path" ]] || [[ "$FORCE" == true ]]; then
|
|
# Build milestones table rows
|
|
ms_table=""
|
|
for i in $(seq 0 $(( MILESTONE_COUNT - 1 ))); do
|
|
ms_id="$(echo "$milestones_json" | jq -r ".[$i].id")"
|
|
ms_name="$(echo "$milestones_json" | jq -r ".[$i].name")"
|
|
ms_table+="| $(( i + 1 )) | $ms_id | $ms_name | pending | — | — | — | — |"$'\n'
|
|
done
|
|
|
|
cat > "$manifest_path" <<EOF
|
|
# Mission Manifest — $NAME
|
|
|
|
> Persistent document tracking full mission scope, status, and session history.
|
|
> Updated by the orchestrator at each phase transition and milestone completion.
|
|
|
|
## Mission
|
|
|
|
**ID:** $MISSION_ID
|
|
**Statement:** $DESCRIPTION
|
|
**Phase:** Intake
|
|
**Current Milestone:** —
|
|
**Progress:** 0 / $MILESTONE_COUNT milestones
|
|
**Status:** active
|
|
**Last Updated:** $(date -u +"%Y-%m-%d %H:%M UTC")
|
|
|
|
## Success Criteria
|
|
|
|
<!-- Define measurable success criteria here -->
|
|
|
|
## Milestones
|
|
|
|
| # | ID | Name | Status | Branch | Issue | Started | Completed |
|
|
|---|-----|------|--------|--------|-------|---------|-----------|
|
|
$ms_table
|
|
## Deployment
|
|
|
|
| Target | URL | Method |
|
|
|--------|-----|--------|
|
|
| — | — | — |
|
|
|
|
## Token Budget
|
|
|
|
| Metric | Value |
|
|
|--------|-------|
|
|
| Budget | — |
|
|
| Used | 0 |
|
|
| Mode | normal |
|
|
|
|
## Session History
|
|
|
|
| Session | Runtime | Started | Duration | Ended Reason | Last Task |
|
|
|---------|---------|---------|----------|--------------|-----------|
|
|
|
|
## Scratchpad
|
|
|
|
Path: \`docs/scratchpads/$MISSION_ID.md\`
|
|
EOF
|
|
fi
|
|
|
|
# ─── Scaffold scratchpad ────────────────────────────────────────────────────
|
|
|
|
sp_dir="$PROJECT/$SCRATCHPAD_DIR"
|
|
sp_file="$sp_dir/$MISSION_ID.md"
|
|
mkdir -p "$sp_dir"
|
|
|
|
if [[ ! -f "$sp_file" ]]; then
|
|
cat > "$sp_file" <<EOF
|
|
# Mission Scratchpad — $NAME
|
|
|
|
> Append-only log. NEVER delete entries. NEVER overwrite sections.
|
|
> This is the orchestrator's working memory across sessions.
|
|
|
|
## Original Mission Prompt
|
|
|
|
\`\`\`
|
|
(Paste the mission prompt here on first session)
|
|
\`\`\`
|
|
|
|
## Planning Decisions
|
|
|
|
## Session Log
|
|
|
|
| Session | Date | Milestone | Tasks Done | Outcome |
|
|
|---------|------|-----------|------------|---------|
|
|
|
|
## Open Questions
|
|
|
|
## Corrections
|
|
EOF
|
|
fi
|
|
|
|
# ─── Scaffold TASKS.md if absent ────────────────────────────────────────────
|
|
|
|
tasks_path="$PROJECT/$TASKS_MD"
|
|
mkdir -p "$(dirname "$tasks_path")"
|
|
|
|
if [[ ! -f "$tasks_path" ]]; then
|
|
cat > "$tasks_path" <<EOF
|
|
# Tasks — $NAME
|
|
|
|
> Single-writer: orchestrator only. Workers read but never modify.
|
|
|
|
| id | status | milestone | description | pr | notes |
|
|
|----|--------|-----------|-------------|----|-------|
|
|
EOF
|
|
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 ""
|
|
echo -e " ${C_CYAN}Mission ID:${C_RESET} $MISSION_ID"
|
|
echo -e " ${C_CYAN}Milestones:${C_RESET} $MILESTONE_COUNT"
|
|
echo -e " ${C_CYAN}State:${C_RESET} $(mission_path "$PROJECT")"
|
|
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: Resume with '$run_cmd' (or launch directly with '$runtime_cmd')."
|