Mission types updated to support external activation sources:
MissionCadence expanded:
- Cron { expression, timezone } — timezone-aware scheduling
- OnEvent { event_pattern } — channel message pattern matching
- OnSystemEvent { source, event_type } — structured events from tools
- Webhook { path, secret } — external HTTP triggers (GitHub, email, etc.)
- Manual — explicit triggering only
The engine defines trigger TYPES. The bridge implements infrastructure
(cron ticker, webhook endpoints, event matchers). GitHub issues, PRs,
email, Slack events all use the generic Webhook cadence — no
special-casing in the engine. Webhook payload injected as
state["trigger_payload"] in the thread's Python context.
Mission struct extended:
- current_focus: what the next thread should work on (evolving)
- approach_history: what we've tried (for adaptation)
- max_threads_per_day / threads_today: daily budget
- last_trigger_payload: webhook/event data for thread context
Plan updated with trigger type table and webhook integration design.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
9.0 KiB
Missions: Goal-Oriented Autonomous Threads
Date: 2026-03-24 Status: Design → Implementation Depends on: Engine v2 Phases 1-6 (all done)
What a Mission Is
A Mission is a Project with intent — a persistent goal that spawns threads, accumulates knowledge, adapts its approach, and tracks progress toward completion.
Unlike routines (fixed prompt, stateless, mechanical), Missions evolve:
- Each thread is informed by all previous threads via Project-scoped MemoryDocs
- The prompt is generated (not fixed) based on accumulated knowledge
- The approach changes when something fails
- The Mission can detect completion
Core Types
pub struct Mission {
pub id: MissionId,
pub project_id: ProjectId,
pub goal: String,
pub status: MissionStatus, // Active, Paused, Completed, Failed
// Trigger
pub cadence: MissionCadence, // Cron, OnEvent, Manual
// Evolving strategy
pub current_focus: Option<String>, // what the next thread should work on
pub approach_history: Vec<String>, // what we've tried
// Progress
pub success_criteria: Option<String>,
pub thread_history: Vec<ThreadId>,
// Budget
pub max_threads_per_day: u32,
pub max_total_threads: Option<u32>,
}
Already defined in crates/ironclaw_engine/src/types/mission.rs.
Trigger Types
The engine defines trigger types. The bridge implements the actual infrastructure:
| Trigger | Engine type | Bridge implementation |
|---|---|---|
| Cron schedule | MissionCadence::Cron { expression, timezone } |
Tokio interval task, cron parser |
| Channel message | MissionCadence::OnEvent { event_pattern } |
Regex match in handle_message before routing |
| System event | MissionCadence::OnSystemEvent { source, event_type } |
Match events from event_emit tool |
| Webhook | MissionCadence::Webhook { path, secret } |
Register HTTP endpoint on webhook server |
| Manual | MissionCadence::Manual |
mission_fire tool or API call |
Webhook-based integrations (GitHub, email, etc.) use the generic Webhook cadence. The webhook payload is stored as mission.last_trigger_payload and injected into the thread's context:
# Inside the mission's thread, the trigger payload is accessible:
payload = state["trigger_payload"]
# For a GitHub webhook: payload["action"], payload["issue"]["title"], etc.
# For email: payload["from"], payload["subject"], payload["body"]
This means GitHub issues, PRs, email, Slack events, etc. all work through the same webhook mechanism — no special-casing in the engine.
Architecture
MissionManager (runtime/mission.rs)
│
├── Cron ticker (tokio interval task)
│ └── For each Active mission with Cron cadence:
│ check if due → spawn_mission_thread()
│
├── Event listener (optional)
│ └── Match event patterns → spawn_mission_thread()
│
└── spawn_mission_thread(mission):
1. Load Project's MemoryDocs (lessons, playbooks, issues)
2. Generate meta-prompt from goal + focus + docs + approach history
3. ThreadManager.spawn_thread_with_history(meta_prompt, ...)
4. join_thread() → outcome
5. Reflection runs automatically (ThreadManager handles this)
6. Update mission: current_focus, approach_history, thread_history
7. Check success criteria → maybe mark Completed
Meta-Prompt Generation
The key differentiator from routines. Before each thread, the Mission builds a prompt:
Goal: {mission.goal}
## What we know (from prior threads)
{retrieved lessons, playbooks, issues from Project MemoryDocs}
## Current focus
{mission.current_focus or "Determine the first step toward the goal"}
## Previous approaches
{mission.approach_history — what we've tried and what happened}
## Instructions
Based on the above context, take the next step toward the goal.
Use tools to gather information, analyze data, or take actions.
When you've completed this step, call FINAL() with:
1. What you accomplished
2. What you recommend as the next focus
3. Whether the goal has been achieved
The response is parsed to extract:
- Accomplishment → becomes a Summary doc
- Next focus → updates
mission.current_focus - Goal achieved → transitions mission to Completed
Implementation Plan
Step 1: MissionManager with cron trigger
crates/ironclaw_engine/src/runtime/mission.rs — already has types, needs execution logic:
MissionManager::new(thread_manager, store)— holds refs to spawn threadsMissionManager::start_cron_ticker()— spawns a tokio task that checks missions every 60sMissionManager::spawn_mission_thread(mission)— the core: build prompt, spawn thread, process resultMissionManager::create_mission(goal, cadence, project_id)— creates and stores a missionMissionManager::pause/resume/cancel_mission(id)— lifecycle management
Step 2: Meta-prompt builder
crates/ironclaw_engine/src/runtime/mission_prompt.rs:
- Load MemoryDocs from project via RetrievalEngine
- Build the structured prompt from mission state + docs
- Parse the thread's FINAL() response to extract next_focus and goal_status
Step 3: Wire into bridge router
src/bridge/router.rs:
EngineStateholdsArc<MissionManager>- On init, load existing missions from store, start cron ticker
- Unblock
mission_create,mission_list,mission_pausetools (or expose as special functions in CodeAct)
Step 4: Mission tools for CodeAct
The model can create and manage missions from code:
# Create a mission
mission_create(
goal="Monitor and improve API response times",
cadence="0 9 * * *", # daily at 9am
success_criteria="p95 latency under 200ms for 7 days"
)
# List missions
missions = mission_list()
# Pause/resume
mission_pause(id="...")
mission_resume(id="...")
Step 5: Progress tracking + adaptation
After each mission thread completes:
- Parse FINAL() for next_focus recommendation
- If the same error appears 3+ times → change approach (add to approach_history, clear current_focus, let the next thread try fresh)
- If success_criteria is met → mark Completed, notify user
- If max_threads exceeded → mark Failed, notify user
Step 6: Mission persistence
Missions stored via Store::save_mission/load_mission/list_missions (trait methods already defined). The HybridStore needs to persist missions to workspace (like MemoryDocs) so they survive restarts.
How This Replaces Routines
| Routine feature | Mission equivalent |
|---|---|
| Cron schedule | MissionCadence::Cron("0 9 * * *") |
| Event trigger | MissionCadence::OnEvent { pattern } |
| Manual fire | MissionCadence::Manual + mission_fire(id) |
| Fixed prompt | Meta-prompt generated from goal + project docs |
| Notification on completion | Thread outcome → channel notification |
| Lightweight execution | Thread with max_iterations: 1 |
| Full job execution | Thread with full iteration budget |
| Guardrails (max concurrent, timeout) | ThreadConfig on spawned threads |
The v1 RoutineEngine can stay for backward compatibility. New missions use the engine v2 MissionManager.
Example: Daily Tech News Briefing
mission_create(
goal="Deliver a daily tech news briefing covering AI, crypto, and software engineering",
cadence="0 8 * * *",
success_criteria=None # ongoing, never "done"
)
Thread 1 (day 1):
- Searches for news, summarizes top stories
- Reflection: Playbook("Use web_search with freshness='pd', then llm_context for details")
Thread 2 (day 2):
- Uses the Playbook from day 1 (faster, more efficient)
- Reflection: Lesson("Bloomberg paywalled, use Reuters/AP instead")
Thread 3 (day 3):
- Avoids Bloomberg (learned), uses Reuters
- Reflection: Lesson("User prefers bullet points over paragraphs")
Each day the briefing improves because the Mission accumulates knowledge.
Example: Improve Test Coverage
mission_create(
goal="Increase IronClaw test coverage from 60% to 80%",
cadence="0 10 * * 1-5", # weekdays at 10am
success_criteria="coverage >= 80% in cargo tarpaulin report"
)
Thread 1: Runs cargo tarpaulin, identifies uncovered modules
Thread 2: Writes tests for the most uncovered module
Thread 3: Runs coverage again, checks progress, picks next module
Thread N: Coverage hits 80% → Mission Completed
What Already Exists vs What's New
| Component | Status |
|---|---|
Mission type + MissionCadence + MissionStatus |
✅ Exists |
Store trait: save/load/list/update missions |
✅ Exists |
MissionManager struct |
✅ Exists (shell) |
ThreadManager.spawn_thread() |
✅ Exists |
RetrievalEngine (project-scoped doc retrieval) |
✅ Exists |
| Reflection pipeline (produces docs) | ✅ Exists |
HybridStore persistence for MemoryDocs |
✅ Exists |
| Cron ticker loop | ❌ NEW |
| Meta-prompt generation from mission state + docs | ❌ NEW |
| FINAL() response parsing for next_focus | ❌ NEW |
| Progress tracking + adaptation | ❌ NEW |
| Mission persistence to workspace | ❌ NEW (extend HybridStore) |
| Mission tools for CodeAct | ❌ NEW |