fix: move mission context to top of system prompt + auto-inject initial prompt

Mission context was buried at the end of a 21K char system prompt and the
agent ignored it. Two fixes:
1. Mission block now emits FIRST in build_runtime_prompt() so it's the most
   prominent instruction the agent sees
2. When an active mission exists and no user prompt is given, auto-inject
   an initial user message triggering the agent to read mission state files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 17:52:44 -06:00
parent 221afe94d9
commit 5d666bdca9

View File

@@ -138,6 +138,44 @@ build_runtime_prompt() {
exit 1
fi
# Inject active mission context FIRST so the agent sees it immediately
local mission_file=".mosaic/orchestrator/mission.json"
if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
local m_status
m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
local m_name m_id m_count m_completed
m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
m_id="$(jq -r '.mission_id // ""' "$mission_file")"
m_count="$(jq '.milestones | length' "$mission_file")"
m_completed="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mission_file")"
cat <<MISSION_EOF
# ACTIVE MISSION — HARD GATE (Read Before Anything Else)
An active orchestration mission exists in this project. This is a BLOCKING requirement.
**Mission:** $m_name
**ID:** $m_id
**Status:** $m_status
**Milestones:** $m_completed / $m_count completed
## MANDATORY — Before ANY Response to the User
You MUST complete these steps before responding to any user message, including simple greetings:
1. Read \`~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md\` (mission lifecycle protocol)
2. Read \`docs/MISSION-MANIFEST.md\` for full mission scope, milestones, and success criteria
3. Read the latest scratchpad in \`docs/scratchpads/\` for session history, decisions, and corrections
4. Read \`docs/TASKS.md\` for current task state (what is done, what is next)
5. After reading all four, acknowledge the mission state to the user before proceeding
If the user gives a task, execute it within the mission context. If no task is given, present mission status and ask how to proceed.
MISSION_EOF
fi
fi
cat <<'EOF'
# Mosaic Launcher Runtime Contract (Hard Gate)
@@ -169,40 +207,6 @@ EOF
printf '\n\n# Runtime-Specific Contract\n\n'
cat "$runtime_file"
# Inject active mission context if present in CWD
local mission_file=".mosaic/orchestrator/mission.json"
if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
local status
status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
if [[ "$status" == "active" || "$status" == "paused" ]]; then
local m_name m_id m_count m_completed
m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
m_id="$(jq -r '.mission_id // ""' "$mission_file")"
m_count="$(jq '.milestones | length' "$mission_file")"
m_completed="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mission_file")"
printf '\n\n# Active Mission Context\n\n'
cat <<MISSION_EOF
An active orchestration mission has been detected in this project.
**Mission:** $m_name
**ID:** $m_id
**Status:** $status
**Milestones:** $m_completed / $m_count completed
## MANDATORY First Actions
1. Load \`~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md\` (mission lifecycle protocol)
2. Read \`docs/MISSION-MANIFEST.md\` for full mission scope, milestones, and success criteria
3. Read \`docs/scratchpads/${m_id}.md\` for session history, decisions, and corrections
4. Read \`docs/TASKS.md\` for current task state (what is done, what is next)
5. Do NOT begin any coding or planning until you have read all four documents above
You are resuming an existing mission. The state files are the source of truth.
MISSION_EOF
fi
fi
}
# Ensure runtime contract is present at the runtime's native config path.
@@ -221,6 +225,22 @@ ensure_runtime_config() {
fi
}
# Detect active mission and return an initial prompt if one exists.
# Sets MOSAIC_MISSION_PROMPT as a side effect.
_detect_mission_prompt() {
MOSAIC_MISSION_PROMPT=""
local mission_file=".mosaic/orchestrator/mission.json"
if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
local m_status
m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
local m_name
m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
MOSAIC_MISSION_PROMPT="Active mission detected: ${m_name}. Read the mission state files and report status."
fi
fi
}
# Launcher functions
launch_claude() {
check_mosaic_home
@@ -234,8 +254,16 @@ launch_claude() {
# Claude supports --append-system-prompt for direct injection
local runtime_prompt
runtime_prompt="$(build_runtime_prompt "claude")"
# If active mission exists and no user prompt was given, inject initial prompt
_detect_mission_prompt
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
echo "[mosaic] Launching Claude Code (active mission detected)..."
exec claude --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
else
echo "[mosaic] Launching Claude Code..."
exec claude --append-system-prompt "$runtime_prompt" "$@"
fi
}
launch_opencode() {
@@ -289,8 +317,15 @@ launch_yolo() {
# Claude uses an explicit dangerous permissions flag.
local runtime_prompt
runtime_prompt="$(build_runtime_prompt "claude")"
_detect_mission_prompt
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
echo "[mosaic] Launching Claude Code in YOLO mode (active mission detected)..."
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
else
echo "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..."
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$@"
fi
;;
codex)
check_mosaic_home