Assync Mission Orchestrator¶
Purpose¶
Replaces the manual "copy result between agents" workflow with a structured, auditable pipeline:
Mission Model (missions/models.py)¶
A Mission is a strictly bounded, typed unit of work: id, project_id,
title, objective, scope_include/scope_exclude, authorization_type,
required_roles, stages, state, risk_level, acceptance_criteria,
allow_optional_run_failure, plus an append-only transition_history.
Created and validated via missions/service.py::create_mission, which
rejects unknown projects, unknown or non-executable roles, missing
acceptance criteria, and authorization_type=ABORT at creation time
(missions/validation.py).
State Machine (missions/state_machine.py)¶
READY → ACTIVE → WAITING_FOR_AUTHORIZATION → ACTIVE → COMPLETED
READY → ABORTED
ACTIVE → BLOCKED | ABORTED
WAITING_FOR_AUTHORIZATION → ABORTED
BLOCKED → ACTIVE | ABORTED
COMPLETED and ABORTED are terminal — no transition is defined out of
either. A transition to COMPLETED is rejected if unresolved blocking
findings remain (has_unresolved_blocking_findings). Every transition is
recorded with previous/next state, timestamp, actor, reason, and an
optional evidence reference — never edited after the fact.
Design note: the canonical transition table has no
WAITING_FOR_AUTHORIZATION → BLOCKED path and no separate
CHANGES_REQUESTED mission state (that concept exists only for
decisions, see decision-queue.md). When a human rejects a
risk-acceptance decision, the mission is explicitly transitioned to
ABORTED — the only valid, already-defined "does not continue" state
reachable from WAITING_FOR_AUTHORIZATION. See
tests/test_e2e_mvp.py::BlockedEndToEndScenarioTest for the full,
verified sequence.
Workflow (orchestrator/service.py::start_mission)¶
- Verify the mission is
READYand the supplied authorization matchesmission.authorization_typeand is still usable. - Acquire the mission lock (
storage/locking.py). - Select roles (
orchestrator/selection.py::select_roles) — mission-required roles plus deterministic, risk-driven policy additions (e.g. medium+ risk addstechnical_reviewer). No LLM is involved in role selection. - Detect separation-of-duty violations across the resolved role→adapter assignments.
- Transition to
ACTIVE. - Execute each selected role's configured adapter sequentially, in role-ID order — the MVP never simulates background/parallel execution.
- Normalize findings (
findings/normalization.py) and run deterministic synthesis (synthesis/engine.py). - If synthesis reports blockers or required human decisions, create a
Decisionand transition toWAITING_FOR_AUTHORIZATION. Otherwise transition straight toCOMPLETED. - Release the mission lock in a
finallyblock — even on an unhandled exception, which additionally transitions the mission toBLOCKEDbefore re-raising.
It never commits, pushes, merges, or releases — no code path in this MVP calls a Git write operation.
Locking (storage/locking.py)¶
One lock per mission, containing mission ID, PID, hostname, owner, and
creation timestamp. A held lock is never silently stolen — acquire_lock
always raises LockConflictError if a lock file exists, even if it looks
stale; only an explicit assync lock clear --force removes it
(is_stale() informs that decision, it never triggers it automatically).
MVP Limitations¶
- Execution is synchronous and single-process; the model field for "parallel" execution exists conceptually in the stage list but has no parallel implementation.
- Retries default to zero and are not yet exposed via CLI.
- There is no persisted "project-level default risk policy" — risk level is set per mission at creation time.