Files
optimclaw/docs/plans/2026-03-24-missions.md
T
[email protected]andClaude Opus 4.6 10cf040b43 feat(engine): extend Mission types with webhook/event triggers + evolving strategy
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]>
2026-03-24 09:21:15 -07:00

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 threads
  • MissionManager::start_cron_ticker() — spawns a tokio task that checks missions every 60s
  • MissionManager::spawn_mission_thread(mission) — the core: build prompt, spawn thread, process result
  • MissionManager::create_mission(goal, cadence, project_id) — creates and stores a mission
  • MissionManager::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:

  • EngineState holds Arc<MissionManager>
  • On init, load existing missions from store, start cron ticker
  • Unblock mission_create, mission_list, mission_pause tools (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:

  1. Parse FINAL() for next_focus recommendation
  2. If the same error appears 3+ times → change approach (add to approach_history, clear current_focus, let the next thread try fresh)
  3. If success_criteria is met → mark Completed, notify user
  4. 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