Skip to content

Repository files navigation

MMA Workflow

Minimal Multi-Agent Workflow runner. It coordinates an executor and one or more reviewers through planning, implementation, testing, and human approval gates.

Default roles: executor = claude (Opus 4.8, effort xhigh) and a two-reviewer panelcodex (GPT-5.5) + claude (Sonnet) — that run in parallel each round. Each role keeps a persistent session across phases, token usage and cost are measured precisely, and an interactive single-process UI shows live agent output.

新手从 docs/TUTORIAL.md 上手 —— 含一个你可以亲自在 human gate 参与的交互式示例:mma run examples/demo_requirement.md --config interactive

工作流程图 / Workflow Diagrams

顶层流程 (Top-level)

flowchart TD
    Start(["mma run req.md"]) --> Init["初始化<br/>解析 config / 解析 workdir<br/>创建 flow/ 与 log/run-id/<br/>启动交互式 UI + 常驻输入框"]
    Init --> P["Planning 阶段<br/>_planning()"]
    P --> HGP{"Human Gate: planning"}
    HGP -->|"go / implement"| I["Implementation 阶段<br/>_implementation()"]
    HGP -->|"反馈文本"| PRB["写 plan_review_human{k}.md<br/>Executor rebuttal → reviewer panel 复审"]
    PRB --> HGP
    HGP -->|"exit / quit"| EX(["安全退出 (终止所有 agent)"])
    I --> HGF{"Human Gate: final"}
    HGF -->|"finish / done"| Done(["phase = finished"])
    HGF -->|"反馈文本"| IRB["写 implementation_test_human{k}.md<br/>Executor rebuttal → reviewer panel 复审"]
    IRB --> HGF
    HGF -->|"exit / quit"| EX
Loading

Planning / Implementation 阶段 (Executor ⇄ Reviewer 对抗)

评审按轮同步进行:每轮所有(尚未通过的)reviewer 并行评审,一次 executor rebuttal 统一回应所有未通过的意见;最多 max_rounds 轮,直到每个 reviewer 都通过(结构化 verdict pass,或 marker 模式下输出 ACCEPT! / TEST PASS!)或轮数耗尽。最后一轮不再 rebuttal(改了也没有 reviewer 复核);若 rebuttal 修改了 plan/代码,已通过的 reviewer 会重新评审新版本。

flowchart TD
    EX["Executor 产出 (plan.md / 代码)"] --> RV["Reviewer 评审 / 实测<br/>→ plan_review{i}.md / implementation_test{i}.md"]
    RV --> OK{"输出含 [REVIEWERx] ACCEPT! / TEST PASS! ?"}
    OK -->|"是"| Pass(["该 reviewer 通过"])
    OK -->|"否"| RB["Executor rebuttal<br/>→ plan_rebuttal{i}.md / implementation_rebuttal{i}.md"]
    RB --> Cnt{"round < max_rounds ?"}
    Cnt -->|"是, round+1"| RV
    Cnt -->|"否"| Warn(["进入 human gate"])
Loading

单次 Agent 调用 (agents.py + backends.py)

flowchart TD
    R["build_command (claude/codex/plain)<br/>首call 用 --session-id;后续 resume<br/>prompt 走 stdin, 子进程流式输出"] --> Parse["按 kind 解析事件流<br/>readable text + 精确 tokens + session id"]
    Parse --> Watch{"子进程结束 / 超时 / exit"}
    Watch -->|"超时·取消"| Kill["终止进程组 (SIGTERM→SIGKILL)"] --> Code
    Watch -->|"正常"| Code{"exit_code == 0 ?"}
    Code -->|"是"| OKr(["AgentResult(text, tokens, cost, session_id)"])
    Code -->|"否, --yes"| Err(["抛错停止"])
    Code -->|"否, 交互"| HP{"human: retry / continue / exit"}
    HP -->|retry| R
    HP -->|continue| OKr
    HP -->|exit| Stop(["退出"])
Loading

Install

pip install -e .

This exposes a global mma command and pulls in prompt_toolkit and rich.

On older Apple system Python/pip, editable install may need the legacy path:

python3 setup.py develop --user

If mma is installed but not on PATH, add the user Python bin dir to PATH or symlink it, e.g.:

ln -s ~/Library/Python/3.9/bin/mma /opt/homebrew/bin/mma

Requires the claude and codex CLIs on PATH for the default config.

Run

Point at a requirement markdown file from any directory. The default config is claude_codex:

mma run /path/to/requirement.md

Equivalent explicit form:

mma run --requirement /path/to/requirement.md --config claude_codex

Config aliases:

  • claude_codex (default): examples/config.claude_codex.json — claude executor (Opus, ultracode) + a parallel reviewer panel (codex GPT-5.5, claude Opus +ultracode, claude Sonnet).
  • interactive: examples/config.interactive.json — snappy sonnet executor + codex reviewer.
  • codex: examples/config.codex.json — codex for both roles.
  • codex_cursor: examples/config.codex_cursor.json.
  • fake / fake2: examples/config.fake.json (1 reviewer) / config.fake2.json (2 reviewers) — offline smoke tests.

Useful flags:

  • --ui {auto,tui,plain} — force the interactive TUI or plain text output (default auto: TUI when attached to a terminal).
  • --yes — auto-approve every human gate (headless / CI).
  • --project <dir> — isolated project folder (created if needed).

Project layout: the project dir defaults to --projectworkdir in config → the requirement file's directory. Each project is self-contained and runs never clobber each other:

  • workspace/ — generated code (the executor's cwd; a clean tree you can git init)
  • flow/<run-id>/plan.md, reviews, rebuttals, and the implementation report (isolated per run)
  • log/<run-id>/ — prompts, raw agent output, status.json, sessions.json
  • requirement.md — a snapshot of the requirement for that run

Resume an interrupted run (skips completed phases, re-attaches agent sessions):

mma resume /path/to/project

Batch a whole directory of tasks — each *.md runs headless as its own isolated project, N at a time; finished tasks are skipped and interrupted ones auto-resumed:

mma queue ./tasks --config claude_codex --concurrency 3

Interactive UI

When run in a terminal, MMA opens a single-process full-screen UI (no tmux required):

  • Dashboard (top): current phase/round and a per-agent table — status, live token counts (in/out), cost, and elapsed time.
  • Output pane (middle): the active agent's readable output, streamed live (assistant text, reasoning, tool/command calls, and -/+ edit diffs), auto-following the tail. Code and tool output are shown dimmed; additions are green, removals red.
  • Input box (bottom): always-on. Enter sends, Ctrl-J inserts a newline, multi-line paste is supported.

Navigating the output pane (Tab focuses it; Tab again returns to the input box):

  • / (or k / j) scroll one line; Enter / Space page forward, b / Backspace page back (PageUp / PageDown too); the mouse wheel scrolls as well. g / Home jump to the top of the current call and freeze; while you are scrolled up the view freezes for reading, and scrolling back to the bottom (or G / End) resumes following the live tail (like less +F). Esc hands the keyboard back to the input box. The pane's border brightens while it has focus, and the status bar switches to the pager key hints.
  • / page through each agent call's full transcript (the header shows which call is on screen via the ●/⏸ N/M tag). Home jumps to the top of the current call and freezes it for reading; End returns to the live tail and resumes auto-following.
  • The pane's title bar shows a scroll readout — L<first>–<last>/<total> with ↑N / ↓N for how many lines are above/below, or ⤓ tail when you are at the bottom — so a short-looking pane is obviously just the tail, not the whole turn. The complete, untruncated transcript of every call is also on disk at log/<run-id>/<NNN>_<agent>_<task>.trajectory.md (and the whole run in trajectory.md).

Gate commands typed into the input box:

  • planning gate: go / implement to proceed, or any other text as feedback (written to flow/plan_review_human<k>.md, one file per feedback round) — the executor rebuts it and the reviewer panel then re-reviews the update before returning to the gate.
  • final gate: finish / done to end, or feedback (written to flow/implementation_test_human<k>.md), with the same rebuttal + re-review loop.
  • review at either gate sends the current work back through the reviewer panel.
  • exit / quit (or Ctrl-C) anywhere terminates all agent processes and exits safely.

Interaction modes (cycle with Ctrl-T, or type full / autopilot / lite):

  • full (default) — reviewers + human gates.
  • autopilot — reviewers run, human gates auto-approve. Toggling it while sitting at a gate applies from the NEXT gate (type go/finish to pass the current one); before auto-finishing, reviewers re-review once if their last round didn't pass unanimously.
  • lite — no reviewers; the executor talks straight to you.

reviewers N typed at any time resizes the reviewer panel for the next review phase (extra reviewers clone the last configured one with fresh sessions).

Reviewer and testing stages always leave a readable artifact in flow/, even when the agent accepts or passes directly.

Token statistics & cost

Token usage is parsed from each CLI's structured output (claude stream-json result.usage + total_cost_usd; codex --json turn.completed.usage), not estimated from characters. Totals accumulate per agent and are shown in the UI and in:

mma metrics --log-dir /path/to/log/<run-id>

Codex's CLI does not report a dollar cost, so MMA shows an estimate computed from its token counts and the hard-coded price table in backends.py (CODEX_PRICES) — always labelled ~$ in the UI. Verify/adjust the table if prices change. Claude's cost is exact (total_cost_usd); if a claude call is killed by timeout/cancel, its tokens are still counted from the streamed events but the dollar cost for that call is unknown.

Persistent sessions

Each role keeps one continuous session across phases (claude via --session-id/--resume, codex via exec resume <thread_id>), so later phases retain earlier reasoning. Session ids are recorded in log/<run-id>/sessions.json. Set "persistent": false on an agent to fall back to stateless one-shot calls.

Config

{
  "max_rounds": 3,
  "agent_timeout_seconds": 1800,
  "ui": "auto",
  "agents": {
    "executor": {
      "name": "executor",
      "kind": "claude",
      "command": "claude",
      "model": "claude-opus-4-8",
      "effort": "xhigh",
      "persistent": true,
      "args": ["--dangerously-skip-permissions"]
    },
    "reviewers": [
      {
        "name": "reviewer1",
        "kind": "codex",
        "command": "codex",
        "model": "gpt-5.5",
        "effort": "xhigh",
        "persistent": true,
        "args": ["--skip-git-repo-check", "--sandbox", "workspace-write", "-C", "{workdir}"]
      }
    ]
  }
}

Flow artifacts are archived by MMA itself from each agent's captured stdout (reviews via the typed verdict, rebuttals via the fallback writer) — agents are told to OUTPUT content, not to write flow/ files, so sandboxed backends work out of the box and the audit trail can't be rewritten by agents. Sandboxes: claude agents ship with --dangerously-skip-permissions; the codex counterpart is --yolo (bypasses approvals and the file sandbox — use it for a codex executor in a trusted personal setup; keep reviewers on --sandbox workspace-write, which is also the safer default).

"rereview_policy": "pending" (default) re-runs only not-yet-accepted reviewers each round — accepted votes stand unless a rebuttal changes the plan/workspace fingerprint; "all" re-runs the WHOLE panel every round with the full dispute history in context (a reviewer may withdraw its accept) — fuller cross-examination at roughly roster× the review cost, sensible with cheap panel models.

Per-agent fields: kind (claude | codex | plain) selects command building and output parsing; model, effort, persistent are optional; "ultracode": true (claude kind) lets the role fan out internal multi-agent workflows — supported and encouraged, with one rule: each MMA call is one CLI process, so the workflow must run synchronously within the turn (run_in_background: false, or block on TaskOutput); background-and-notify and scheduled wakeups die at the turn boundary (the executor prompts carry this usage note); args are extra CLI flags ({workspace}, {project}, {flow_dir}, {mma_root} are substituted; {workdir} is an alias for {workspace}). Add more reviewers by appending to reviewers; they run concurrently each round and a single executor rebuttal addresses all of them. Set "parallel_reviewers": false (or --no-parallel) to run them sequentially. Measured: 2 reviewers run ~1.5× faster in parallel; the gap widens with more reviewers.

Structured verdicts & lessons (default in claude_codex): with "verdict": "structured" (or --verdict structured) reviewers emit a schema-validated verdict ({decision, blocking_issues, tests_run, notes}) via claude --json-schema / codex --output-schema instead of a text marker — the pass/changes decision is a typed enum, not a substring grep (text markers remain the fallback). With "record_lessons": true (or --record-lessons) each run appends its reviewers' blocking issues to <project>/LESSONS.md, which — together with an optional global "lessons_file" — is injected into future agent prompts as reference: a self-improving loop. When an agent hits a transient failure (e.g. a dropped API connection), MMA first retries by resuming the same session with a short continue-nudge — the context is already loaded there, so it costs a fraction of a fresh session — and only falls back to a fresh session if that fails too. When retries are exhausted (or on a timeout), MMA stops at the current stage and (in interactive runs) lets you fix things and type retry, continue, or exit.

Status (headless)

During a run, status is written to log/<run-id>/status.json. Render it from another shell:

mma status --log-dir /path/to/log/<run-id>

Test

Unit tests:

python3 -m pytest

Offline fake workflow smoke test:

tmpdir=$(mktemp -d)
printf '# Requirement\n\nCreate a fake plan.\n' > "$tmpdir/req.md"
mma run --requirement "$tmpdir/req.md" --config fake --yes

Benchmarks

See docs/BENCHMARKS.md for controlled single-agent vs MMA A/B tests. Headline: on precise tasks a strong model already scores 100% (MMA = verification), but with a weak executor or an ambiguous/hard spec the reviewer panel lifts the score (e.g. haiku 85→91 on terse semver ranges, 72→74 on the spreadsheet task), and a cross-vendor panel catches different bugs.

References

docs/references/ contains navigation maps of the two upstream projects used to design this tool:

  • claude-code-best.md — Claude Code's single-process TUI, session storage, and token accounting.
  • openai-codex.md — Codex's exec --json event protocol, session rollouts/resume, and token usage.

See ROADMAP.md for planned work (live interruption, reviewer parallelism, configurable workflow graph).

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages