In the v4.5.0 review round grok read the brief and published `started` 70 s after the injection; with T = 60 the wrapper had already closed the job as agent_blocked and refused its later PASS. T bounds delivery, not work, so a generous default costs nothing when the agent answers fast. Code, docs and the CLI snapshot updated together. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
11 KiB
name, description, version, author, license, platforms, environments, metadata
| name | description | version | author | license | platforms | environments | metadata | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| multi-agent-mux-delegate-job | Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, grok-build, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer. | 4.5.0 | godopu | MIT |
|
|
|
multi-agent-mux-delegate-job — Async Job Delegation over MQTT
Delegate a unit of work to any autonomous agent, then observe it asynchronously instead of blocking. Every job gets a unique ID and a registry record. The worker agent publishes lifecycle events (started, permission_required, progress, completed, error) to a per-job MQTT topic, and the delegator/orchestrator subscribes to verify the final state.
This skill allows any agent (claude-code, hermes, agy, grok-build, etc.) to play any role: Orchestrator/Delegator, Worker/Implementer, or Reviewer.
Roles in Multi-Agent Mux
- Orchestrator (Delegator): Initiates the job, coordinates other agents, handles loops and reviews, and commits final changes.
- Worker (Implementer): Receives the brief file or task prompt, performs the implementation, and emits started/completed/error events.
- Reviewer: Evaluates git diffs or artifacts produced by the worker, and responds with a
completedevent containing"PASS"or feedback.
Core Commands (CLI)
The multi-agent-mux-delegate-job bash wrapper handles job registration, subscriber management, agent session targeting, and validation hooks:
# 1) Submit a new job to a targeted agent session (e.g. herdr session name 'demo')
multi-agent-mux-delegate-job submit \
--agent <claude-code|hermes-agent|agy-agent|grok-build|human> \
--agent-session herdr:<session_name> \
--prompt "Task description or instructions here" \
--role <Worker|Planner|Reviewer> \
--timeout 3600 [--heartbeat 30 --heartbeat-misses 4 --ack-timeout 180] [--idle-timeout <sec>]
# Liveness contract (Issue #18): the agent heartbeats every N s (automatic),
# the job is unresponsive after K x N s (--idle-timeout overrides), `started`
# must arrive within T s of the injection or the inject fails closed.
# 1b) Same, for an agent on another host (no herdr pane, no shared disk):
# the brief travels over the broker as a signed retained dispatch and a
# `remote.py run --session <name>` runner on that host executes it.
multi-agent-mux-delegate-job submit --agent remote --agent-session remote:<runner_session> \
--prompt "Task description" --role Worker # needs MAM_DISPATCH_SECRET on both hosts
# 2) Submit a job with a feedback loop (Worker-Reviewer Loop)
multi-agent-mux-delegate-job submit \
--agent <worker_agent> --agent-session herdr:<worker_session> \
--type loop --reviewer <reviewer_agent> --reviewer-session herdr:<reviewer_session> \
--prompt "Task description"
# 3) Check job status and audit logs
multi-agent-mux-delegate-job status --job <JOB_ID>
multi-agent-mux-delegate-job logs <JOB_ID> # Chronological log of events
multi-agent-mux-delegate-job list # Summary of all registered jobs
# 4) Verify job artifacts with a validation script
multi-agent-mux-delegate-job verify --job <JOB_ID> --validate ./validate.sh
Task Delegation Types
Supported job types include:
direct(default): Single agent execution (direct tasking).loop(Worker-Reviewer Loop): Alternates worker execution and reviewer evaluation until reviewer approves (PASS) or iterations run out.discuss(Research & Discussion): Collaboration between two agents to reach a consensus (e.g., agreeing on a design or plan).
For detailed state machine diagrams and configurations, see DELEGATION_TYPES.md.
Exit Codes & Failure Handling
Under fail-closed operation (MAM_DELEGATE_FAIL_CLOSED=1, default), the CLI returns deterministic exit codes:
| Exit Code | Meaning | Final Registry Status | Failure Behavior & Resilience |
|---|---|---|---|
0 |
Success | completed |
Job succeeded and completed event received or verified on disk with approval (PASS/AGREE). |
1 |
Failure | error |
Execution failure / delivery failure: no started within --ack-timeout T s after the injection (agent_blocked; the prompt is never re-injected), or the input never reached the pane. |
2 |
Timeout | error (deadline_timeout) |
Subscriber timeout (total budget or idle timeout reached without terminal event). R1 liveness grace (Issue #18): after the subscriber idles out the wrapper keeps re-observing the disk while the job's events.log shows an event at most K x N (--idle-timeout) old - the automatic heartbeat counts - up to the wall-clock --timeout. herdr status and the pane are logged as a diagnostic only. |
3 |
Broker Error | error (deadline_timeout) |
Broker unavailable (MQTT_BROKER_UNAVAILABLE) and terminal status not found on disk during deadline. |
5 |
Session Busy | cancelled (session_busy) |
Precondition failed / session_busy: Preflight check (preflight_agent) or TUI idle handshake wait failed (prompt never delivered, retryable). |
In all execution paths, the final line of standard output prints the persistent audit log directory path (<logs_root>/<job_id>), fulfilling the parsing contract for orchestrator runners.
Where the Implementation Lives (v4.3.0)
The implementation moved to the lib_py.delegation package in issue #5 phase 1.
scripts/{mqtt_common,registry,publish_event,job_subscriber}.py remain as
compatibility shims and their paths are unchanged -- every command in this
document still works verbatim. Each shim binds the lib_py.delegation package
to the tree next to itself, once per process, without touching sys.path, so
import mqtt_common yields the same module object as
lib_py.delegation.mqtt_common.
Which tree wins, by execution context:
| Context | Resolved by | Tree that wins |
|---|---|---|
Loop process and its children (frozen lib.sh sourced) |
the helper's explicit bootstrap from SKILL_DIR |
the frozen snapshot |
Agent session running a frozen script path while PYTHONPATH still points live |
the shim, from its own location | the frozen snapshot (B-13 preserved) |
| Live helpers (create/stop/resume/reconcile) | PYTHONPATH or the shim (same tree) |
the live tree |
Standalone, no PYTHONPATH |
the shim's fallback append | the shim's own tree |
lib.sh calls the package through _delegation_py, which inserts its own
SKILL_DIR at the front of the import path. It deliberately does not use
python -m: for -m the current directory precedes PYTHONPATH, so a frozen
loop whose working directory happened to contain a lib_py package would have
silently executed live code. The working directory is left untouched; the
registry and audit-log paths resolve to MAM_JOBS_DIR / DELEGATE_JOB_LOGS_DIR
when set, otherwise to $WORKSPACE_ROOT/.mam/jobs and
$WORKSPACE_ROOT/.mam/delegate_job_logs (the workspace lib.sh exports), and
only without either to the cwd-relative .mam/... (Issue #15 — this is what
stops a caller whose cwd is not the workspace, such as the pytest suite, from
registering jobs into a foreign registry).
Setting MAM_DELEGATION_PY=0 switches the shell helpers back to their preserved
legacy bodies with no redeploy.
One in-process caveat: the package binds once per process and is never rebound,
so do not import a frozen shim after a live lib_py.delegation is already bound
inside the same interpreter. Production never does this — each delegated job is
a fresh interpreter — but a test harness could.
The Event Protocol Contract
Every agent participating in the delegation contract must follow the same lifecycle publishing protocol using publish_event.py:
- On Start (first thing, within the ack timeout T): Publish
startedand arm the heartbeat timer.python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --job "$JOB_ID" --event started --heartbeat <N>The timer publishesprogressevery N s until your terminal event (Issue #18); the brief and the injected instructions carry the exact command. - On Tool/Permission Prompt: Publish
permission_requiredevent.python3 ... --job "$JOB_ID" --event permission_required --detail "<tool>:<reason>" - On Progress Update: Publish
progressat check-points (the heartbeat timer already satisfies the every-N-seconds obligation).python3 ... --job "$JOB_ID" --event progress --detail "<status_update>" - On Success: Publish
completedevent.python3 ... --job "$JOB_ID" --event completed --detail "<summary>"(Reviewer should include"PASS"in the detail to approve). - On Failure/Feedback: Publish
errorevent.python3 ... --job "$JOB_ID" --event error --detail "<reason_or_feedback>"
Audit Logs
Job lifecycle execution events are persistently mirrored to an append-only log under .mam/delegate_job_logs/<job_id>/ (containing meta.json, events.ndjson, and status.json). Use multi-agent-mux-delegate-job logs <job_id> to view the timeline.
Best Practices and Pitfalls
- Subscribe-Before-Publish: The subscriber must be running before the agent starts publishing. The
submitcommand handles this automatically by launching the subscriber in the background first. - Fresh job_id Propagation: Make sure the worker agent receives the correct
JOB_IDgenerated for the current run, rather than reusing stale IDs from previous sessions. - Brief delivery via file path: For long or complex prompts, write the instructions to a file (e.g.
/tmp/task-brief.md) and pass a short prompt pointing to the file path to prevent terminal buffer overflows. - Prompts injected into a live agent session MUST be English, ASCII-only, and short — this is exactly what
--prompt/theinstructionsstring sent torun_agent()end up as. Any Korean (or other non-ASCII) content the task needs to convey must go in a markdown brief file (e.g..mam/jobs/<id>/brief.md, written in Korean is fine) that the injected prompt merely tells the agent to read. Two independent bugs insend_keys_safe's paste-verification (inlib.sh) made this matter in practice: (a) its marker was taken with a byte-basedtail -c 24, which can slice a multi-byte UTF-8 (e.g. Korean) character in half; (b) the rendered pane soft-wraps long lines at the terminal width, which can split the marker across two visual lines. Both are now fixed at the source (character-safe truncation + newline-stripped matching before comparison), but keeping injected prompts short/English/file-referencing is still the cheapest way to avoid ever exercising this edge case at all — it's also simply whatsubmit's own default instruction template already does (seeCore Commandsabove). - Batch Grouping: Group non-overlapping tasks into batches to parallelize execution across multiple agent sessions, reducing overhead.