Hera — native multi-agent coordination inside argus
Hera is argus's native, in-tree layer for running a team of agents. It is not a separate
daemon or plugin — coordination runs in-process in the argus daemon, the rail/tree render directly
in the TUI's second tab, and agents drive it entirely through the mcp__argus__hera_* MCP
tools. State lives in the same ~/.argus/data.sql (the hera_* tables). You never touch the
plumbing; you call the tools.
1. When this applies (and when it does NOT)
The mcp__argus__hera_* tools are only registered inside an argus task sandbox. You are in one if
either holds:
ARGUS_TASK_ID is set, or
- the current working directory is under
~/.argus/worktrees/.
If neither holds, stop. The tools are not registered in this session — there is no CLI fallback and
nothing below applies.
Sandbox residency alone is NOT a reason to use hera. Most argus sessions are plain solo tasks that
should stay solo. The "coordinate via hera, never hand-roll it" imperative in this skill applies only
once this session already has evidence of being hera-managed:
- it was spawned as a hera worker — its prompt carries the orientation prefix naming the
coordinator + orchestrator (see
hera_spawn_worker's prompt contract in §3), or
- it already holds, or is actively creating (e.g. via
hera_new_orchestrator), a
coordinator/freelance binding for this session.
No such evidence? This is a bare argus task the human is driving directly. Don't assume hera and
don't self-promote into a coordinator just because the work could be split up — at most, mention hera as
an available option for multi-session work ("this could be split into a hera team with its own
worktrees/PRs per stage if you want — say so, or I'll keep it in this session") and only act on it if
they opt in. Once you're spawned/promoted per the bullets above, everything below governs.
Every hera tool takes cwd — always pass cwd=$PWD. That is how hera resolves which argus task
(and therefore which role) this session is. There is no separate "auth" or session handle.
2. The role model
- Orchestrator — a named coordination graph (one per project / feature / wave). Roles live under
it. Created by
hera_new_orchestrator (idempotent by name).
- Coordinator role — the orchestrator's driver. Created by
hera_new_orchestrator. Talks to the
human in its own agent pane; talks to other roles via hera_send. Folded into the rail's
orchestrator header (it is not a separate row).
- Worker role — does the actual work. Normally born-bound: spawned by a coordinator with
hera_spawn_worker, which creates the argus task (worktree + session) AND the role + binding in one
transaction. A born-bound worker does not need to hera_join — its session opens already bound.
- Freelance role — a helper that attaches itself to an existing orchestrator on its own
initiative (no coordinator spawned it). Created via
hera_join attach mode with kind=freelance.
- Binding — the live link between this argus task and one role. Invariants:
- One live binding per
(task, orchestrator) — enforced by a partial unique index.
- A task MAY hold several live bindings at once — one per orchestrator (e.g. a worker in A that
promotes itself to coordinator of nested orchestrator B). When a task holds 2+ live bindings,
you must pass
orchestrator=<name> to every tool so hera knows which role you are acting as;
omitting it returns an ambiguity error listing your options.
3. The coordination tools
All take cwd. orchestrator is optional with exactly one live binding and required with 2+.
Arg names below are exact — do not invent others. These eleven cover bootstrap, messaging, status,
revive, and completion; the plan-DAG authoring/mutation tools live in the companion hera-plan skill
(pointer at the end of this section).
Bootstrap / join
hera_new_orchestrator(cwd, name, coordinator_role_name, [prompt]) — "I am the coordinator."
Creates (or fetches, idempotent-by-name) the orchestrator, creates the named coordinator role, and
binds this task to it. Returns the orchestrator name, role name, binding_id, and argus task id.
Rejects if this task already holds a live binding under that orchestrator (use hera_join to
retrieve it). The canonical "become an orchestrator" entry point — don't hera_join first; there is
nothing to join yet.
hera_join(cwd, [orchestrator], [role_name], [kind], [prompt], [status]) — two modes:
- Claim mode (
role_name omitted) — retrieves this task's existing live binding + role and its
unread message count. Use right after a born-bound worker terminal opens to read your assigned
role/mission. Pass orchestrator= if the task has 2+ bindings. Claim mode does not consume the
inbox.
- Attach mode (
role_name + kind supplied) — creates a new role under an existing
orchestrator (orchestrator required) and binds this task to it. kind must be worker or
freelance (coordinator is rejected — use hera_new_orchestrator). Optional prompt (stored on
the role) and status (idle/working/blocked/done). Use to join a team nobody spawned you
into.
hera_spawn_worker(cwd, prompt, [orchestrator], [role_name], [project], [branch], [backend], [model], [archetype])
— spawn a new born-bound worker task + session under the caller's orchestrator. Caller must hold
a live coordinator binding. Creates an argus task (worktree + session) and, transactionally, a worker
role + binding pre-bound to it; an orientation prefix naming the coordinator + orchestrator is prepended
to the prompt automatically. Args:
prompt (required) — the worker's MISSION/task only. The verbatim prompt is also stored on the
role row and shown as the node's description in the plan-DAG view. Do NOT prepend the org/security
policy — see the mission-only gotcha in §6.
project — defaults to the coordinator's own task project (authoritative, not role.ArgusProject).
branch — base branch passed to argus task creation. Defaults to the project default — see the
base-branch gotcha in §6.
backend — defaults to project default.
model — per-worker model override, scoped to the worker's resolved backend (claude: opus/sonnet/
haiku; codex: e.g. gpt-5; pi: its ids). Empty = backend default. Match it to task complexity. Leave it unset when you pass archetype= so the archetype picks the tier — see §9.
archetype — the worker's diligence archetype (e.g. code_slice, bug_fix, review, ci_loop;
defaults to code_slice). Selects the per-archetype model from the project's bound profile and is
exported to the worker as ARGUS_ARCHETYPE. An explicit model still wins over the profile. See §9.
role_name — derived from a prompt slug if omitted; uniquified within the orchestrator.
orchestrator — disambiguates when the calling task holds multiple live coordinator bindings.
Returns the orchestrator, worker role name, binding_id, argus task id, and project.
Messaging (idle-gated bus)
hera_send(cwd, body, tldr, status, [to], [in_reply_to], [orchestrator]) — message another role
in the same orchestrator. body and tldr are required; tldr is a one-line summary ≤120
chars, written from the recipient's perspective (see §5). status is REQUIRED for worker/freelance
senders (one of idle/working/blocked/done/failed) and is applied to the sender's role
synchronously before the message is sent — it is never delivered async. Omitting status as a
worker/freelance sender is an error; coordinator senders may omit it. Worker/freelance senders may
omit to — it default-routes to the orchestrator's coordinator. Coordinators must supply an
explicit to. in_reply_to threads a reply to a prior message id. Returns the message_id,
recipient, and delivery mode. Caps: 64 KiB body, 500 unread per recipient, 50 sends/min/sender.
Cross-orchestrator messaging is not possible — to always resolves within your own orchestrator.
hera_inbox(cwd, [orchestrator]) — fetch all unread messages addressed to your role, oldest
first. Reading IS acknowledgment: this both cancels pending pane deliveries AND marks the
messages read — no separate hera_mark_read needed for normal consumption. Call it whenever you get a
doorbell.
hera_mark_read(cwd, message_ids, [orchestrator]) — explicitly mark specific message ids read and
cancel their pending deliveries. Use when you read via hera_get_messages instead of hera_inbox.
Status / tree
hera_status(cwd, status, [orchestrator]) — set your role status: idle | working | blocked
| done | failed. Mirrored (best-effort) to argus task_meta so the coordinator sees it without
asking. A worker-kind role reporting status=done also rolls its bound argus task to in_review
and stamps ready_to_close (visible in the rail) — see §4. A worker reporting status=failed
rolls its task to in_review WITHOUT ready_to_close (needs-attention, not ready to check off).
The gater treats a failed blocker as explicitly failed (no need to wait for session death).
Coordinators/freelancers just update status.
hera_revive(cwd, role_name, [orchestrator]) — coordinator-only PULL-revive of one role you
coordinate. Reach for this when a role you spawned looks stuck — hera_tree_updates/hera_status
show no progress, especially after something like a session-supervisor restart (which SIGHUPs every
PTY it owns, leaving a worker dead or suspended). It inspects the role's live session and takes
exactly one action: a dead session is restarted in place; a live-but-genuinely-stuck session (idle,
NOT parked at a prompt) is kicked (stopped and resumed in place); anything else — busy, blocked on a
question, a live coordinator, or a kick already in flight — is left untouched and reported as such
(skipped_busy / skipped_blocked_on_prompt / skipped_coordinator_live / skipped_restart_pending).
This is pull-only — nothing calls it automatically, and it can never thrash a session that is
actually working or waiting on an answer, since it applies the identical idle+not-blocked gate the
TUI's own Enter-key revive uses. It targets a DIFFERENT role than your own (self-targeting errors).
hera_accept(cwd, role_name, [orchestrator], [message]) — coordinator-only: mark a role's bound
task complete and send it a check-in asking whether it's winding down, has more work to do, or is
unsure. The reply is informational only — never auto-reopens the task; a premature accept is undone
via hera_revive alone. Never stops the session (completion and detachment are separate). Acts from
any non-complete status; a no-op if already complete. The plan-DAG gater fires this automatically
for blockers when dependent nodes materialize — use this tool for accepting work outside that flow
(ad hoc spawns, or roles with no plan-DAG dependents).
hera_tree_updates(cwd, [orchestrator], [since]) — scan the caller's orchestrator subtree
(nested sub-orchestrators included) for messages since a cursor. Returns TLDR-only subject lines —
no bodies (capped at 200), plus a next_cursor. The cursor is stored per-role and auto-advances
when you omit since; passing an explicit since is a one-off scan that does NOT clobber the stored
cursor. The token-efficient way to see whole-team activity without flooding context with bodies.
hera_get_messages(cwd, ids, [orchestrator]) — fetch full message bodies by id list, after
scanning hera_tree_updates. Access is scoped to the caller's orchestrator subtree (sender OR
recipient must live in it); inaccessible / missing ids get a per-id error field rather than a
top-level error.
Plan-DAG authoring (staged, dependency-ordered work) → the hera-plan skill
With a live coordinator binding, do NOT use the harness TaskCreate system-reminder for
coordinated work — coordinate via the hera tools (hera_spawn_worker, or the plan-DAG), never
bare task creation.
For work that decomposes into multiple hera worker units with dependencies among them (one stage
needs another's output, or a required ordering), hera offers a plan-DAG: planned nodes wired by
blocking edges that the daemon gater materializes into born-bound workers in dependency order,
auto-stacking each stage's branch on the prior. It renders in the TUI's second tab and is a living
graph (edit / re-point / cancel as work evolves).
Load the companion hera-plan skill before authoring or driving a plan-DAG. It carries the six
plan tools (hera_plan, hera_plan_node, hera_block, hera_plan_node_update, hera_unblock,
hera_plan_node_cancel), the gating contract (a node materializes ONLY when every blocker reaches
role-status done), automatic branch-stacking and its fan-in footgun, short-id node naming,
sub-coordinator nodes, and the self-guard prompt patterns — none of which the tool schemas convey. The
decision triad in §4 tells you when the plan-DAG is the right tool versus spawning workers directly
or using in-session sub-agents.
4. Decision rules
- Starting a coordination effort?
hera_new_orchestrator. Don't hera_join first.
- A coordinator spawned you (fresh born-bound worker terminal)?
hera_join(cwd) to read your role
- mission, then
hera_status(working). You are already bound — no attach needed.
- Joining a team that didn't spawn you?
hera_join attach mode with explicit role_name + kind.
new_orchestrator vs join: new_orchestrator makes you a coordinator of a new orchestrator;
join claims/attaches a role under an existing one. A worker can do BOTH — stay a worker in the
parent and hera_new_orchestrator to become a coordinator of a nested team (multi-binding).
spawn_worker vs adopt: native hera has no adopt step — workers are born bound at spawn time.
(The old depends_on-driven auto-adopt watcher was retired with the DAG.) To delegate, just
hera_spawn_worker.
- Never
hera_move your OWN coordinator binding to join another team. hera_move's kind is
worker/freelance only — moving away from a live coordinator role ends that binding and orphans
the whole orchestrator/subtree you were coordinating, leaving a disconnected freelance/worker stub
under the target with no link back to it (hera-freelancer-bug; the tool now rejects this outright).
There is no agent-facing tool to nest an existing coordinator + subtree under a new parent — that's
the Hera TUI's J (adopt/reparent) key, human-only. If a human wants your whole team folded under
another coordinator, tell them to press J on your orchestrator; don't try to self-relocate.
- The coordination decision — in-session sub-agents vs hera workers vs the plan-DAG (settle this
BEFORE delegating). Delegate with prejudice, but don't be dumb about it:
- Ephemeral, in-session work — research, review, fan-out reads, anything that returns results
to you and needs no worktree/PR of its own? → use Claude's native sub-agents (Agent/Task
tool). NOT hera. Hera is overkill and slower for in-session parallelism. But don't reflexively
round-trip a sub-agent for every read: a single small file or a one-shot grep with a few hits is
cheaper read inline than dispatched — delegate when the exploration volume clearly dwarfs the
answer needed back, not on principle.
- Work whose unit must be its OWN argus session — separate worktree / its own PR / long-running
/ its own sandbox? → hera. Then split by dependency:
- Units have dependencies among them (one needs another's output, or a required ordering)? →
plan-DAG: load the
hera-plan skill, author planned nodes + blocking edges, let the gater
run them in dependency order. Decide this yourself when the dependency is obvious — don't ask
the human. Internal dependencies are the clean signal to plan a DAG rather than spawn ad hoc.
- Units are independent (no ordering)? → just
hera_spawn_worker them in parallel; no DAG.
- Ask the human only when it's genuinely ambiguous whether the work warrants multi-session
orchestration at all — never for the routine dependency call above.
- Prefer a hera worker over a native sub-agent for anything that must survive a coordinator
recycle. Only a hera-tracked session comes back cleanly after a recycle (self-service, or the
coord-hook's automatic forced recycle past 1.5x token budget, which has no idle gate at all) — a
native sub-agent is just an in-process tool call invisible to the daemon, and gets killed along
with the rest of your session with no trace. Don't call
hera_status(request_recycle=true) while
a native sub-agent you dispatched is still running: self-service recycle only defers until you go
idle, and a backgrounded sub-agent produces zero output while it works, so you look idle when you
aren't — requesting recycle early kills its work mid-flight. If you must hand off or record status
while one is still in flight, say so in handoff_note so a human reviewing status doesn't
force-recycle blindly.
- This task holds 2+ bindings? Pass
orchestrator= on EVERY tool call.
- Got a doorbell? Call
hera_inbox(cwd=$PWD) immediately — the content is in the inbox, not the
doorbell line.
- Want whole-team state?
hera_tree_updates(cwd=$PWD), then hera_get_messages(ids=[…]) for the
ones worth reading.
- A role looks stuck (no progress, especially after a session-supervisor restart)? Don't spawn a
duplicate worker on a hunch — try
hera_revive(cwd=$PWD, role_name=<name>) first. It's a safe,
idle+not-blocked-gated no-op if the role turns out to be fine, busy, or waiting on a question.
- How completion flows back: a worker finishing sends a closing
hera_send(status="done", …) — the
synchronous status apply rolls its task to in_review + ready_to_close, visible in the rail.
This is the worker's self-report, NOT task closure. Once you (the coordinator) have independently
verified the outcome (the PR actually merged, CI green, deliverables shipped—not just taken the worker's
word for it), call hera_accept(cwd=$PWD, role_name=<name>) in the SAME turn. Don't wait to be
asked, and don't treat a status message alone as sufficient closure. A worker that cannot complete
sends hera_send(status="failed", …) — rolls to in_review WITHOUT ready_to_close (needs
attention, not ready to check off); the gater holds any dependent planned nodes and pings you. Both
rolls are idempotent and only fire when the task is still in_progress. The live session is left
running.
- Don't use
hera_send to talk to the human — the human reads the coordinator's own agent pane;
the bus is role-to-role only.
5. TLDR discipline
Every hera_send requires a tldr ≤120 chars, written from the recipient's perspective. It is shown
in the doorbell, returned by hera_tree_updates, and stored permanently.
- Good:
"PR #47 open, tests green, needs review" / "Blocked on missing API key — need rotation"
- Bad:
"update" (says nothing) / "Done with the work" (no specifics) / multi-line.
6. Gotchas worth calling out
hera_send requires status for worker/freelance senders — omitting it is an error. The status
is applied synchronously before the send completes; it never rides the async delivery bus. This means
every hera_send call doubles as a role-status heartbeat. There is no default; the error message on
omission names the valid values (idle/working/blocked/done/failed).
- Spawned workers default to the project's stale default branch, NOT the coordinator's branch.
hera_spawn_worker's branch defaults to the project default (e.g. an old master/main), not the
coordinator's current worktree branch. If the worker must build on the coordinator's (or a sibling's)
work, pass branch= explicitly and verify ancestry — otherwise the worker starts from stale code.
- Pass the MISSION only in
hera_spawn_worker / hera_plan_node prompts — never prepend the
org/security policy. A hera worker is a full argus session that receives its org instructions
independently, via its OWN session's harness injection (an <organizationInstructions> block, verified
present in every spawned session regardless of the parent). So a manually prepended copy is a redundant
duplicate — and argus stores the prompt verbatim on the role and renders its opening lines as the node's
description in the plan-DAG, so a prepended policy pollutes the DAG (every node reads the boilerplate
instead of its mission). Write the prompt as exactly the message you want the worker to act on.
- Bake all requirements into the initial
hera_spawn_worker prompt. Mid-flight hera_send to a
worker is often missed: delivery is idle-gated and best-effort, so a busy worker never receives it and
an idle/finished worker may not act on it. Put the full spec in the spawn prompt; verify via the branch
diff and re-dispatch a fresh worker if one idled out without the requirement.
- The message bus is idle-gated and best-effort for delivery, durable for storage. Storage always
succeeds (the row is committed); live pane delivery soft-fails (logged, never rolled back) when the
recipient has no live binding or never becomes idle.
hera_inbox always returns the durable rows, so
the recipient can always catch up by reading — don't assume a sent message was seen just because it sent.
worker done must keep the role messageable. Native deliberately does NOT auto-archive a role on
hera_status(done) (the external plugin did). An archived role drops out of name-keyed recipient
resolution, so auto-archiving a still-live worker would make the coordinator's hera_send to it bounce.
Done flips the task to in_review; it does not archive the role.
- Coordinators must name a recipient.
hera_send from a coordinator with to omitted errors —
only worker/freelance senders get the default-to-coordinator routing.
7. Worked workflows
(a) Bootstrap an orchestrator, spawn two workers, collect results
You are a coordinator-to-be in your argus sandbox:
hera_new_orchestrator(cwd=$PWD, name="checkout-revamp", coordinator_role_name="coord", prompt="Coordinate the checkout revamp").
- Spawn workers, each with the FULL spec baked in and an explicit base branch:
hera_spawn_worker(cwd=$PWD, role_name="cart-api", branch="argus/<base>", prompt="<complete cart-API spec…>")
hera_spawn_worker(cwd=$PWD, role_name="checkout-ui", branch="argus/<base>", prompt="<complete checkout-UI spec…>")
- Poll progress without flooding context:
hera_tree_updates(cwd=$PWD) → scan TLDRs →
hera_get_messages(cwd=$PWD, ids=[…]) for the interesting ones.
- When a worker reports
done (its task rolls to in_review + ready_to_close in the rail), review its
branch/PR independently (verify the PR merged, tests actually green, etc.). Once satisfied, call
hera_accept(cwd=$PWD, role_name=<worker-name>) to mark the task complete, then reply or spawn the
next stage. To stack work, branch the next worker off the prior worker's branch via branch=.
(b) A spawned worker reports completion
You opened in a born-bound worker terminal:
hera_join(cwd=$PWD) → read your role name, mission (role prompt), and unread count.
hera_status(cwd=$PWD, status="working").
- Do the work in your worktree. If you hit a fork that needs the coordinator's call:
hera_send(cwd=$PWD, status="working", body="<question + context>", tldr="Need decision: X vs Y for the cart schema")
(no to needed — default-routes to the coordinator), then check hera_inbox(cwd=$PWD) on the
doorbell for the answer. Always supply status on every hera_send — it is required for
worker/freelance senders.
- Land your work (open a PR via iris, or leave commits for the coordinator to pull).
hera_send(cwd=$PWD, status="done", body="<summary + PR link>", tldr="cart-api done, PR #47, tests green")
— the synchronous status apply rolls your task to in_review + ready_to_close so the coordinator
sees you finished. If you cannot complete: hera_send(cwd=$PWD, status="failed", body="<reason>", …).
(c) Author a staged plan-DAG
When the work decomposes into multiple multi-session units with dependencies among them, load the
hera-plan skill and author a plan-DAG — it carries the tools, gating, branch-stacking, naming, and
a full worked seed→fan-out→fan-in example. The §4 decision triad tells you when that's the right tool.
Worker promotion: becoming a sub-coordinator
If you already know up front (at plan-authoring time) that a stage is a sub-team, prefer the
declarative form: a kind=subcoord plan node (see the hera-plan skill). The gater then
materializes the sub-coordinator for you when its blockers finish. The runtime promotion below is for
when a worker discovers the need mid-task.
If a worker realizes mid-task it needs its own team (cross-repo work, real parallelism, a long sub-task):
hera_new_orchestrator(cwd=$PWD, name="<sub-team>", coordinator_role_name="coord", prompt="…") — now
this session is a coordinator of a nested orchestrator AND still a worker in the parent (multi-binding;
pass orchestrator= on subsequent calls).
hera_spawn_worker(...) to dispatch into the right project.
- Report the sub-orchestrator name back to the parent coordinator via
hera_send. Prefer using Claude's
native sub-agents for in-session parallelism; reserve hera_spawn_worker for work where the session
itself (separate worktree / repo / sandbox) is the unit.
8. Composition with sibling argus tools
Hera owns identity, messaging, and coordination — nothing else. Reach the rest through their own MCP
tools:
- iris (
mcp__argus__iris_*) — host-side git/gh. A worker codes + commits locally in its worktree,
then uses iris to push / open a PR / merge back. Use iris_gh_pr_create rather than gh pr create
so the PR is stamped onto the task's pr meta namespace — that is what the Hera rail's PR indicator
reads (best-effort, never fetched by the view).
- plannotator-argus (
mcp__argus__plannotator_*) — review UI. A coordinator routes a worker's output
to review there; hera carries the decision and the handoff message, plannotator carries the review
surface.
These are orthogonal — hera does not wrap them and they do not wrap hera. Pick per op: iris when an action
touches the host, plannotator when it's a review surface, hera when it's about roles or messaging.
9. Diligence-profile awareness (ARGUS_PROFILE / ARGUS_ARCHETYPE)
Argus can route model choice per archetype via diligence profiles — named, on-disk presets that
map an archetype (what kind of job a task is) to a model/effort/window. Resolution happens daemon-side
at spawn, outside your sandbox; the result is handed to you as environment variables, not files. You
do not load or parse any profile — you only read the env.
What you can read (when set):
ARGUS_ARCHETYPE — your task's archetype: one of brainstorm, orchestrator, big_build,
code_slice, bug_fix, review, security_review, synthesis, spec_audit, ci_loop, verify,
recovery, docs. It tells you what kind of job this is — a code_slice worker should behave like a
focused implementer, a review worker like a reviewer, a ci_loop worker like a mechanical green-the-
build loop. Treat it as a hint about the diligence expected of you, not a hard contract.
ARGUS_PROFILE — the name of the bound profile that drove the choice (e.g. default, lean,
customer_grade).
ARGUS_MODEL — the model the profile selected for your archetype.
Critical: all three are exported together or not at all, and may be absent even when you have an
archetype. They are exported only when a bound profile actively contributed a backend-valid model.
If the project has no bound profile, the profile is missing/invalid, your archetype isn't mapped, or the
profile's model isn't valid for your backend, resolution fails open (the agent runs on its own CLI
default) and none of the three vars are set. So: read ARGUS_ARCHETYPE if present for a behavior
hint, but never assume it exists, and never block on it.
You do not consult profile files. Reading ~/.argus/profiles/ from inside the sandbox can EPERM;
that is exactly why resolution runs daemon-side and arrives by env. If you spawn workers yourself, pass
archetype= on hera_spawn_worker (and on plan-DAG nodes — see the hera-plan skill) to set their
archetype; you do not set your own.
Let the archetype resolve the model: omit model on hera_spawn_worker whenever you pass archetype=,
so the profile's per-archetype tier actually takes effect (precedence is task.Model → profile[archetype].model → backend default — an explicit model always wins over the profile). Only pass an explicit model
alongside archetype= when you deliberately want to override that tier for this one spawn — otherwise you
silently defeat the archetype's whole purpose (e.g. a ci_loop worker spawned with model="opus" throws away
the cheap tier ci_loop exists to select, and just runs at full price with no signal anything went wrong).
Reviewer panels are NOT driven here (deferred). A customer_grade-style profile may carry a [panel]
reviewer block, but this skill does not consume it — composing and running a reviewer panel is owned by
the sibling 2a-xvendor-review capability and is not wired up yet. Do not attempt to assemble a review
panel from the profile; treat [panel] as out of scope for now.
1---2name: hera3description: Inside an argus sandbox (cwd under ~/.argus/worktrees/ or ARGUS_TASK_ID set), coordinate multi-agent work via hera's mcp__argus__hera_* tools — bootstrap an orchestrator, claim or attach a worker/freelance role, spawn workers, and message roles over the idle-gated bus. Load this skill when you are (or are becoming) a coordinator, when you're spawned as a hera worker, or before delegating work that must run as its OWN argus session (separate worktree / long-running agent / own PR / own sandbox) — as opposed to in-process Claude sub-agents, which stay the right tool for ephemeral in-session fan-out. For staged or dependency-ordered multi-worker plans, this skill routes you to the companion `hera-plan` skill. NOT for non-argus sessions, where these MCP tools are not registered.4---56# Hera — native multi-agent coordination inside argus78Hera is argus's **native, in-tree** layer for running a *team* of agents. It is not a separate9daemon or plugin — coordination runs in-process in the argus daemon, the rail/tree render directly10in the TUI's second tab, and agents drive it entirely through the `mcp__argus__hera_*` MCP11tools. State lives in the same `~/.argus/data.sql` (the `hera_*` tables). You never touch the12plumbing; you call the tools.1314## 1. When this applies (and when it does NOT)1516The `mcp__argus__hera_*` tools are only *registered* inside an argus task sandbox. You are in one if17**either** holds:1819- `ARGUS_TASK_ID` is set, **or**20- the current working directory is under `~/.argus/worktrees/`.2122**If neither holds, stop.** The tools are not registered in this session — there is no CLI fallback and23nothing below applies.2425**Sandbox residency alone is NOT a reason to use hera.** Most argus sessions are plain solo tasks that26should stay solo. The "coordinate via hera, never hand-roll it" imperative in this skill applies only27once this session already has evidence of being hera-managed:2829- it was **spawned as a hera worker** — its prompt carries the orientation prefix naming the30 coordinator + orchestrator (see `hera_spawn_worker`'s prompt contract in §3), or31- it already **holds, or is actively creating** (e.g. via `hera_new_orchestrator`), a32 coordinator/freelance binding for this session.3334**No such evidence?** This is a bare argus task the human is driving directly. Don't assume hera and35don't self-promote into a coordinator just because the work could be split up — at most, mention hera as36an available option for multi-session work ("this could be split into a hera team with its own37worktrees/PRs per stage if you want — say so, or I'll keep it in this session") and only act on it if38they opt in. Once you're spawned/promoted per the bullets above, everything below governs.3940**Every hera tool takes `cwd` — always pass `cwd=$PWD`.** That is how hera resolves which argus task41(and therefore which role) this session is. There is no separate "auth" or session handle.4243## 2. The role model4445- **Orchestrator** — a named coordination graph (one per project / feature / wave). Roles live under46 it. Created by `hera_new_orchestrator` (idempotent by name).47- **Coordinator role** — the orchestrator's driver. Created by `hera_new_orchestrator`. Talks to the48 human in its own agent pane; talks to other roles via `hera_send`. Folded into the rail's49 orchestrator header (it is not a separate row).50- **Worker role** — does the actual work. Normally **born-bound**: spawned by a coordinator with51 `hera_spawn_worker`, which creates the argus task (worktree + session) AND the role + binding in one52 transaction. A born-bound worker does *not* need to `hera_join` — its session opens already bound.53- **Freelance role** — a helper that attaches itself to an existing orchestrator on its own54 initiative (no coordinator spawned it). Created via `hera_join` attach mode with `kind=freelance`.55- **Binding** — the live link between *this argus task* and *one role*. Invariants:56 - **One live binding per `(task, orchestrator)`** — enforced by a partial unique index.57 - **A task MAY hold several live bindings at once** — one per orchestrator (e.g. a worker in A that58 promotes itself to coordinator of nested orchestrator B). When a task holds **2+** live bindings,59 you **must** pass `orchestrator=<name>` to every tool so hera knows which role you are acting as;60 omitting it returns an ambiguity error listing your options.6162## 3. The coordination tools6364All take `cwd`. `orchestrator` is optional with exactly one live binding and **required** with 2+.65Arg names below are exact — do not invent others. These eleven cover bootstrap, messaging, status,66revive, and completion; the plan-DAG authoring/mutation tools live in the companion `hera-plan` skill67(pointer at the end of this section).6869### Bootstrap / join7071- **`hera_new_orchestrator(cwd, name, coordinator_role_name, [prompt])`** — "I am the coordinator."72 Creates (or fetches, idempotent-by-name) the orchestrator, creates the named coordinator role, and73 binds this task to it. Returns the orchestrator name, role name, `binding_id`, and argus task id.74 Rejects if this task already holds a live binding under that orchestrator (use `hera_join` to75 retrieve it). The canonical "become an orchestrator" entry point — don't `hera_join` first; there is76 nothing to join yet.7778- **`hera_join(cwd, [orchestrator], [role_name], [kind], [prompt], [status])`** — two modes:79 - **Claim mode** (`role_name` omitted) — retrieves this task's existing live binding + role and its80 unread message count. Use right after a born-bound worker terminal opens to read your assigned81 role/mission. Pass `orchestrator=` if the task has 2+ bindings. Claim mode does **not** consume the82 inbox.83 - **Attach mode** (`role_name` + `kind` supplied) — creates a **new** role under an *existing*84 orchestrator (`orchestrator` required) and binds this task to it. `kind` must be `worker` or85 `freelance` (`coordinator` is rejected — use `hera_new_orchestrator`). Optional `prompt` (stored on86 the role) and `status` (`idle`/`working`/`blocked`/`done`). Use to join a team nobody spawned you87 into.8889- **`hera_spawn_worker(cwd, prompt, [orchestrator], [role_name], [project], [branch], [backend], [model], [archetype])`**90 — spawn a new **born-bound** worker task + session under the caller's orchestrator. **Caller must hold91 a live coordinator binding.** Creates an argus task (worktree + session) and, transactionally, a worker92 role + binding pre-bound to it; an orientation prefix naming the coordinator + orchestrator is prepended93 to the prompt automatically. Args:94 - `prompt` (**required**) — the worker's MISSION/task only. The verbatim prompt is also stored on the95 role row and shown as the node's description in the plan-DAG view. **Do NOT prepend the org/security96 policy** — see the mission-only gotcha in §6.97 - `project` — defaults to the **coordinator's own task project** (authoritative, not `role.ArgusProject`).98 - `branch` — base branch passed to argus task creation. **Defaults to the project default — see the99 base-branch gotcha in §6.**100 - `backend` — defaults to project default.101 - `model` — per-worker model override, scoped to the worker's resolved backend (claude: opus/sonnet/102 haiku; codex: e.g. gpt-5; pi: its ids). Empty = backend default. Match it to task complexity. Leave it unset when you pass `archetype=` so the archetype picks the tier — see §9.103 - `archetype` — the worker's **diligence archetype** (e.g. `code_slice`, `bug_fix`, `review`, `ci_loop`;104 defaults to `code_slice`). Selects the per-archetype model from the project's bound profile and is105 exported to the worker as `ARGUS_ARCHETYPE`. An explicit `model` still wins over the profile. See §9.106 - `role_name` — derived from a prompt slug if omitted; uniquified within the orchestrator.107 - `orchestrator` — disambiguates when the calling task holds multiple live coordinator bindings.108109 Returns the orchestrator, worker role name, `binding_id`, argus task id, and project.110111### Messaging (idle-gated bus)112113- **`hera_send(cwd, body, tldr, status, [to], [in_reply_to], [orchestrator])`** — message another role114 **in the same orchestrator**. `body` and `tldr` are **required**; `tldr` is a one-line summary ≤120115 chars, written from the recipient's perspective (see §5). **`status` is REQUIRED for worker/freelance116 senders** (one of `idle`/`working`/`blocked`/`done`/`failed`) and is applied to the sender's role117 **synchronously** before the message is sent — it is never delivered async. Omitting `status` as a118 worker/freelance sender is an error; coordinator senders may omit it. Worker/freelance senders may119 omit `to` — it default-routes to the orchestrator's coordinator. **Coordinators must supply an120 explicit `to`.** `in_reply_to` threads a reply to a prior message id. Returns the `message_id`,121 recipient, and delivery mode. Caps: 64 KiB body, 500 unread per recipient, 50 sends/min/sender.122 Cross-orchestrator messaging is not possible — `to` always resolves within your own orchestrator.123124- **`hera_inbox(cwd, [orchestrator])`** — fetch all unread messages addressed to your role, oldest125 first. **Reading IS acknowledgment**: this both cancels pending pane deliveries AND marks the126 messages read — no separate `hera_mark_read` needed for normal consumption. Call it whenever you get a127 doorbell.128129- **`hera_mark_read(cwd, message_ids, [orchestrator])`** — explicitly mark specific message ids read and130 cancel their pending deliveries. Use when you read via `hera_get_messages` instead of `hera_inbox`.131132### Status / tree133134- **`hera_status(cwd, status, [orchestrator])`** — set your role status: `idle` | `working` | `blocked`135 | `done` | `failed`. Mirrored (best-effort) to argus `task_meta` so the coordinator sees it without136 asking. **A `worker`-kind role reporting `status=done` also rolls its bound argus task to `in_review`137 and stamps `ready_to_close`** (visible in the rail) — see §4. **A worker reporting `status=failed`138 rolls its task to `in_review` WITHOUT `ready_to_close`** (needs-attention, not ready to check off).139 The gater treats a `failed` blocker as explicitly failed (no need to wait for session death).140 Coordinators/freelancers just update status.141142- **`hera_revive(cwd, role_name, [orchestrator])`** — coordinator-only PULL-revive of one role you143 coordinate. Reach for this when a role you spawned looks stuck — `hera_tree_updates`/`hera_status`144 show no progress, especially after something like a session-supervisor restart (which SIGHUPs every145 PTY it owns, leaving a worker dead or suspended). It inspects the role's live session and takes146 exactly one action: a dead session is restarted in place; a live-but-genuinely-stuck session (idle,147 NOT parked at a prompt) is kicked (stopped and resumed in place); anything else — busy, blocked on a148 question, a live coordinator, or a kick already in flight — is left untouched and reported as such149 (`skipped_busy` / `skipped_blocked_on_prompt` / `skipped_coordinator_live` / `skipped_restart_pending`).150 This is **pull-only** — nothing calls it automatically, and it can never thrash a session that is151 actually working or waiting on an answer, since it applies the identical idle+not-blocked gate the152 TUI's own `Enter`-key revive uses. It targets a DIFFERENT role than your own (self-targeting errors).153154- **`hera_accept(cwd, role_name, [orchestrator], [message])`** — coordinator-only: mark a role's bound155 task complete and send it a check-in asking whether it's winding down, has more work to do, or is156 unsure. The reply is informational only — never auto-reopens the task; a premature accept is undone157 via `hera_revive` alone. Never stops the session (completion and detachment are separate). Acts from158 any non-complete status; a no-op if already complete. The plan-DAG gater fires this automatically159 for blockers when dependent nodes materialize — use this tool for accepting work *outside* that flow160 (ad hoc spawns, or roles with no plan-DAG dependents).161162- **`hera_tree_updates(cwd, [orchestrator], [since])`** — scan the caller's orchestrator **subtree**163 (nested sub-orchestrators included) for messages since a cursor. Returns **TLDR-only subject lines —164 no bodies** (capped at 200), plus a `next_cursor`. The cursor is stored **per-role** and auto-advances165 when you omit `since`; passing an explicit `since` is a one-off scan that does NOT clobber the stored166 cursor. The token-efficient way to see whole-team activity without flooding context with bodies.167168- **`hera_get_messages(cwd, ids, [orchestrator])`** — fetch full message bodies by id list, after169 scanning `hera_tree_updates`. Access is scoped to the caller's orchestrator **subtree** (sender OR170 recipient must live in it); inaccessible / missing ids get a per-id `error` field rather than a171 top-level error.172173### Plan-DAG authoring (staged, dependency-ordered work) → the `hera-plan` skill174175> **With a live coordinator binding, do NOT use the harness `TaskCreate` system-reminder for176> coordinated work — coordinate via the hera tools (`hera_spawn_worker`, or the plan-DAG), never177> bare task creation.**178179For work that decomposes into multiple hera worker units **with dependencies among them** (one stage180needs another's output, or a required ordering), hera offers a **plan-DAG**: planned nodes wired by181blocking edges that the daemon gater materializes into born-bound workers in dependency order,182auto-stacking each stage's branch on the prior. It renders in the TUI's second tab and is a *living*183graph (edit / re-point / cancel as work evolves).184185**Load the companion `hera-plan` skill** before authoring or driving a plan-DAG. It carries the six186plan tools (`hera_plan`, `hera_plan_node`, `hera_block`, `hera_plan_node_update`, `hera_unblock`,187`hera_plan_node_cancel`), the gating contract (a node materializes ONLY when every blocker reaches188role-status `done`), automatic branch-stacking and its fan-in footgun, short-id node naming,189sub-coordinator nodes, and the self-guard prompt patterns — none of which the tool schemas convey. The190decision triad in §4 tells you *when* the plan-DAG is the right tool versus spawning workers directly191or using in-session sub-agents.192193## 4. Decision rules194195- **Starting a coordination effort?** `hera_new_orchestrator`. Don't `hera_join` first.196- **A coordinator spawned you (fresh born-bound worker terminal)?** `hera_join(cwd)` to read your role197 + mission, then `hera_status(working)`. You are already bound — no attach needed.198- **Joining a team that didn't spawn you?** `hera_join` attach mode with explicit `role_name` + `kind`.199- **`new_orchestrator` vs `join`:** `new_orchestrator` makes you a coordinator of a *new* orchestrator;200 `join` claims/attaches a role under an *existing* one. A worker can do BOTH — stay a worker in the201 parent and `hera_new_orchestrator` to become a coordinator of a nested team (multi-binding).202- **`spawn_worker` vs adopt:** native hera has **no adopt step** — workers are born bound at spawn time.203 (The old `depends_on`-driven auto-adopt watcher was retired with the DAG.) To delegate, just204 `hera_spawn_worker`.205- **Never `hera_move` your OWN coordinator binding to join another team.** `hera_move`'s `kind` is206 `worker`/`freelance` only — moving away from a live coordinator role ends that binding and orphans207 the whole orchestrator/subtree you were coordinating, leaving a disconnected freelance/worker stub208 under the target with no link back to it (hera-freelancer-bug; the tool now rejects this outright).209 There is no agent-facing tool to nest an existing coordinator + subtree under a new parent — that's210 the Hera TUI's `J` (adopt/reparent) key, human-only. If a human wants your whole team folded under211 another coordinator, tell them to press `J` on your orchestrator; don't try to self-relocate.212- **The coordination decision — in-session sub-agents vs hera workers vs the plan-DAG (settle this213 BEFORE delegating). Delegate with prejudice, but don't be dumb about it:**214 1. **Ephemeral, in-session work** — research, review, fan-out reads, anything that returns results215 to you and needs no worktree/PR of its own? → use **Claude's native sub-agents** (Agent/Task216 tool). NOT hera. Hera is overkill and slower for in-session parallelism. But don't reflexively217 round-trip a sub-agent for every read: a single small file or a one-shot grep with a few hits is218 cheaper read inline than dispatched — delegate when the exploration volume clearly dwarfs the219 answer needed back, not on principle.220 2. **Work whose unit must be its OWN argus session** — separate worktree / its own PR / long-running221 / its own sandbox? → **hera**. Then split by dependency:222 - Units have **dependencies among them** (one needs another's output, or a required ordering)? →223 **plan-DAG**: load the `hera-plan` skill, author planned nodes + blocking edges, let the gater224 run them in dependency order. *Decide this yourself when the dependency is obvious — don't ask225 the human.* **Internal dependencies are the clean signal to plan a DAG** rather than spawn ad hoc.226 - Units are **independent** (no ordering)? → just `hera_spawn_worker` them in parallel; no DAG.227 3. **Ask the human only** when it's genuinely ambiguous whether the work warrants multi-session228 orchestration at all — never for the routine dependency call above.229 4. **Prefer a hera worker over a native sub-agent for anything that must survive a coordinator230 recycle.** Only a hera-tracked session comes back cleanly after a recycle (self-service, or the231 coord-hook's automatic forced recycle past 1.5x token budget, which has no idle gate at all) — a232 native sub-agent is just an in-process tool call invisible to the daemon, and gets killed along233 with the rest of your session with no trace. Don't call `hera_status(request_recycle=true)` while234 a native sub-agent you dispatched is still running: self-service recycle only defers until you go235 idle, and a backgrounded sub-agent produces zero output while it works, so you look idle when you236 aren't — requesting recycle early kills its work mid-flight. If you must hand off or record status237 while one is still in flight, say so in `handoff_note` so a human reviewing status doesn't238 force-recycle blindly.239- **This task holds 2+ bindings?** Pass `orchestrator=` on EVERY tool call.240- **Got a doorbell?** Call `hera_inbox(cwd=$PWD)` immediately — the content is in the inbox, not the241 doorbell line.242- **Want whole-team state?** `hera_tree_updates(cwd=$PWD)`, then `hera_get_messages(ids=[…])` for the243 ones worth reading.244- **A role looks stuck (no progress, especially after a session-supervisor restart)?** Don't spawn a245 duplicate worker on a hunch — try `hera_revive(cwd=$PWD, role_name=<name>)` first. It's a safe,246 idle+not-blocked-gated no-op if the role turns out to be fine, busy, or waiting on a question.247- **How completion flows back:** a worker finishing sends a closing `hera_send(status="done", …)` — the248 synchronous status apply rolls its task to `in_review` + `ready_to_close`, visible in the rail.249 **This is the worker's self-report, NOT task closure.** Once you (the coordinator) have independently250 verified the outcome (the PR actually merged, CI green, deliverables shipped—not just taken the worker's251 word for it), **call `hera_accept(cwd=$PWD, role_name=<name>)` in the SAME turn.** Don't wait to be252 asked, and don't treat a status message alone as sufficient closure. A worker that cannot complete253 sends `hera_send(status="failed", …)` — rolls to `in_review` WITHOUT `ready_to_close` (needs254 attention, not ready to check off); the gater holds any dependent planned nodes and pings you. Both255 rolls are idempotent and only fire when the task is still `in_progress`. The live session is left256 running.257- **Don't** use `hera_send` to talk to the human — the human reads the coordinator's own agent pane;258 the bus is role-to-role only.259260## 5. TLDR discipline261262Every `hera_send` requires a `tldr` ≤120 chars, written from the recipient's perspective. It is shown263in the doorbell, returned by `hera_tree_updates`, and stored permanently.264265- Good: `"PR #47 open, tests green, needs review"` / `"Blocked on missing API key — need rotation"`266- Bad: `"update"` (says nothing) / `"Done with the work"` (no specifics) / multi-line.267268## 6. Gotchas worth calling out269270- **`hera_send` requires `status` for worker/freelance senders — omitting it is an error.** The status271 is applied synchronously before the send completes; it never rides the async delivery bus. This means272 every `hera_send` call doubles as a role-status heartbeat. There is no default; the error message on273 omission names the valid values (`idle`/`working`/`blocked`/`done`/`failed`).274- **Spawned workers default to the project's stale default branch, NOT the coordinator's branch.**275 `hera_spawn_worker`'s `branch` defaults to the *project* default (e.g. an old `master`/`main`), not the276 coordinator's current worktree branch. If the worker must build on the coordinator's (or a sibling's)277 work, **pass `branch=` explicitly and verify ancestry** — otherwise the worker starts from stale code.278- **Pass the MISSION only in `hera_spawn_worker` / `hera_plan_node` prompts — never prepend the279 org/security policy.** A hera worker is a full argus session that receives its org instructions280 independently, via its OWN session's harness injection (an `<organizationInstructions>` block, verified281 present in every spawned session regardless of the parent). So a manually prepended copy is a redundant282 duplicate — and argus stores the prompt verbatim on the role and renders its opening lines as the node's283 description in the plan-DAG, so a prepended policy pollutes the DAG (every node reads the boilerplate284 instead of its mission). Write the prompt as exactly the message you want the worker to act on.285- **Bake all requirements into the initial `hera_spawn_worker` prompt.** Mid-flight `hera_send` to a286 worker is often missed: delivery is idle-gated and best-effort, so a busy worker never receives it and287 an idle/finished worker may not act on it. Put the full spec in the spawn prompt; verify via the branch288 diff and re-dispatch a fresh worker if one idled out without the requirement.289- **The message bus is idle-gated and best-effort for *delivery*, durable for *storage*.** Storage always290 succeeds (the row is committed); live pane delivery soft-fails (logged, never rolled back) when the291 recipient has no live binding or never becomes idle. `hera_inbox` always returns the durable rows, so292 the recipient can always catch up by reading — don't assume a sent message was seen just because it sent.293- **`worker done` must keep the role messageable.** Native deliberately does NOT auto-archive a role on294 `hera_status(done)` (the external plugin did). An archived role drops out of name-keyed recipient295 resolution, so auto-archiving a still-live worker would make the coordinator's `hera_send` to it bounce.296 Done flips the task to in_review; it does not archive the role.297- **Coordinators must name a recipient.** `hera_send` from a coordinator with `to` omitted errors —298 only worker/freelance senders get the default-to-coordinator routing.299300## 7. Worked workflows301302### (a) Bootstrap an orchestrator, spawn two workers, collect results303304You are a coordinator-to-be in your argus sandbox:3053061. `hera_new_orchestrator(cwd=$PWD, name="checkout-revamp", coordinator_role_name="coord", prompt="Coordinate the checkout revamp")`.3072. Spawn workers, each with the FULL spec baked in and an explicit base branch:308 - `hera_spawn_worker(cwd=$PWD, role_name="cart-api", branch="argus/<base>", prompt="<complete cart-API spec…>")`309 - `hera_spawn_worker(cwd=$PWD, role_name="checkout-ui", branch="argus/<base>", prompt="<complete checkout-UI spec…>")`3103. Poll progress without flooding context: `hera_tree_updates(cwd=$PWD)` → scan TLDRs →311 `hera_get_messages(cwd=$PWD, ids=[…])` for the interesting ones.3124. When a worker reports `done` (its task rolls to in_review + ready_to_close in the rail), review its313 branch/PR independently (verify the PR merged, tests actually green, etc.). Once satisfied, call314 `hera_accept(cwd=$PWD, role_name=<worker-name>)` to mark the task complete, then reply or spawn the315 next stage. To stack work, branch the next worker off the prior worker's branch via `branch=`.316317### (b) A spawned worker reports completion318319You opened in a born-bound worker terminal:3203211. `hera_join(cwd=$PWD)` → read your role name, mission (role prompt), and unread count.3222. `hera_status(cwd=$PWD, status="working")`.3233. Do the work in your worktree. If you hit a fork that needs the coordinator's call:324 `hera_send(cwd=$PWD, status="working", body="<question + context>", tldr="Need decision: X vs Y for the cart schema")`325 (no `to` needed — default-routes to the coordinator), then check `hera_inbox(cwd=$PWD)` on the326 doorbell for the answer. **Always supply `status` on every `hera_send` — it is required for327 worker/freelance senders.**3284. Land your work (open a PR via iris, or leave commits for the coordinator to pull).3295. `hera_send(cwd=$PWD, status="done", body="<summary + PR link>", tldr="cart-api done, PR #47, tests green")`330 — the synchronous status apply rolls your task to in_review + ready_to_close so the coordinator331 sees you finished. If you cannot complete: `hera_send(cwd=$PWD, status="failed", body="<reason>", …)`.332333### (c) Author a staged plan-DAG334335When the work decomposes into multiple multi-session units with dependencies among them, **load the336`hera-plan` skill** and author a plan-DAG — it carries the tools, gating, branch-stacking, naming, and337a full worked seed→fan-out→fan-in example. The §4 decision triad tells you when that's the right tool.338339### Worker promotion: becoming a sub-coordinator340341> If you already know up front (at plan-authoring time) that a stage is a sub-team, prefer the342> **declarative** form: a `kind=subcoord` plan node (see the `hera-plan` skill). The gater then343> materializes the sub-coordinator for you when its blockers finish. The runtime promotion below is for344> when a worker discovers the need *mid-task*.345346If a worker realizes mid-task it needs its own team (cross-repo work, real parallelism, a long sub-task):3473481. `hera_new_orchestrator(cwd=$PWD, name="<sub-team>", coordinator_role_name="coord", prompt="…")` — now349 this session is a coordinator of a nested orchestrator AND still a worker in the parent (multi-binding;350 pass `orchestrator=` on subsequent calls).3512. `hera_spawn_worker(...)` to dispatch into the right project.3523. Report the sub-orchestrator name back to the parent coordinator via `hera_send`. Prefer using Claude's353 native sub-agents for in-session parallelism; reserve `hera_spawn_worker` for work where the session354 itself (separate worktree / repo / sandbox) is the unit.355356## 8. Composition with sibling argus tools357358Hera owns **identity, messaging, and coordination** — nothing else. Reach the rest through their own MCP359tools:360361- **iris** (`mcp__argus__iris_*`) — host-side git/gh. A worker codes + commits locally in its worktree,362 then uses iris to push / open a PR / merge back. **Use `iris_gh_pr_create` rather than `gh pr create`**363 so the PR is stamped onto the task's `pr` meta namespace — that is what the Hera rail's PR indicator364 reads (best-effort, never fetched by the view).365- **plannotator-argus** (`mcp__argus__plannotator_*`) — review UI. A coordinator routes a worker's output366 to review there; hera carries the *decision* and the *handoff message*, plannotator carries the *review367 surface*.368369These are orthogonal — hera does not wrap them and they do not wrap hera. Pick per op: iris when an action370touches the host, plannotator when it's a review surface, hera when it's about roles or messaging.371372## 9. Diligence-profile awareness (`ARGUS_PROFILE` / `ARGUS_ARCHETYPE`)373374Argus can route model choice **per archetype** via *diligence profiles* — named, on-disk presets that375map an archetype (what kind of job a task is) to a model/effort/window. Resolution happens **daemon-side376at spawn**, outside your sandbox; the result is handed to you as **environment variables**, not files. You377do not load or parse any profile — you only read the env.378379**What you can read (when set):**380381- **`ARGUS_ARCHETYPE`** — your task's archetype: one of `brainstorm`, `orchestrator`, `big_build`,382 `code_slice`, `bug_fix`, `review`, `security_review`, `synthesis`, `spec_audit`, `ci_loop`, `verify`,383 `recovery`, `docs`. It tells you *what kind of job this is* — a `code_slice` worker should behave like a384 focused implementer, a `review` worker like a reviewer, a `ci_loop` worker like a mechanical green-the-385 build loop. Treat it as a hint about the diligence expected of you, not a hard contract.386- **`ARGUS_PROFILE`** — the name of the bound profile that drove the choice (e.g. `default`, `lean`,387 `customer_grade`).388- **`ARGUS_MODEL`** — the model the profile selected for your archetype.389390**Critical: all three are exported together or not at all, and may be absent even when you have an391archetype.** They are exported **only** when a bound profile actively contributed a backend-valid model.392If the project has no bound profile, the profile is missing/invalid, your archetype isn't mapped, or the393profile's model isn't valid for your backend, resolution *fails open* (the agent runs on its own CLI394default) and **none** of the three vars are set. So: read `ARGUS_ARCHETYPE` if present for a behavior395hint, but never assume it exists, and never block on it.396397**You do not consult profile files.** Reading `~/.argus/profiles/` from inside the sandbox can `EPERM`;398that is exactly why resolution runs daemon-side and arrives by env. If you spawn workers yourself, pass399`archetype=` on `hera_spawn_worker` (and on plan-DAG nodes — see the `hera-plan` skill) to set *their*400archetype; you do not set your own.401402Let the archetype resolve the model: omit `model` on `hera_spawn_worker` whenever you pass `archetype=`,403so the profile's per-archetype tier actually takes effect (precedence is `task.Model → profile[archetype].model404→ backend default` — an explicit `model` always wins over the profile). Only pass an explicit `model`405alongside `archetype=` when you deliberately want to override that tier for this one spawn — otherwise you406silently defeat the archetype's whole purpose (e.g. a `ci_loop` worker spawned with `model="opus"` throws away407the cheap tier `ci_loop` exists to select, and just runs at full price with no signal anything went wrong).408409**Reviewer panels are NOT driven here (deferred).** A `customer_grade`-style profile may carry a `[panel]`410reviewer block, but **this skill does not consume it** — composing and running a reviewer panel is owned by411the sibling `2a-xvendor-review` capability and is not wired up yet. Do not attempt to assemble a review412panel from the profile; treat `[panel]` as out of scope for now.