--- name: multi-agent-mux-create description: "Create a new agent session (claude, antigravity/agy) in a dedicated herdr session for context-preserving long-running work. Always creates a herdr session — never backgrounds with nohup/disown. Writes the new session to .mam/agent-sessions.yaml. Use when you want to start a fresh agent (no prior UUID) for a new project workspace." version: 4.8.9 author: godopu license: MIT platforms: [linux, macos] environments: [terminal, herdr] metadata: hermes: tags: [agent, herdr, claude, antigravity, agy, multi-agent, context, session] related_skills: [multi-agent-mux-resume, multi-agent-mux-stop, multi-agent-mux-monitor, claude-code] prereq_skills: [claude-code] --- # Multi-Agent Create — Start a Fresh Agent in a herdr Session > **Companion skills**: `multi-agent-mux-resume` (resume an existing UUID), `multi-agent-mux-stop` (terminate), `multi-agent-mux-monitor` (live status). > **Single source of truth**: `./.mam/agent-sessions.yaml` (this skill writes to it; never read it ad-hoc — go through this skill). ## What this skill does Spawn a new agent (`claude` or `agy`/antigravity-cli) in a **dedicated herdr session** for context-preserving long-running work. The herdr session is the *container*; the agent's session ID is *data* inside the container. **This skill creates the container + starts the agent — but does not resume an old conversation** (use `multi-agent-mux-resume` for that). For all agents: the herdr session name is produced by **`lib.sh::derive_session_name`** — the single source of truth shared by create/resume/stop/status/monitor (P0-A). The rule (verbatim from the function): > slug = the **two trailing path components** of the absolute workspace, `_`→`-`, lowercased, joined with `-`; name = `-creator-`. So `$WORKSPACE_ROOT/landing_page/refer_landing_page` + `claude` → `landing-page-refer-landing-page-creator-claude`. The workspace basename (`refer_landing_page`) **is** included; the hand-written historical entry that dropped it (`lab-landing-page-creator-claude`) was the bug, not the convention. ## Pre-flight checks `create_session.sh` checks the environment itself and exits 1 if anything is missing: herdr installed (`has_real_herdr`), the agent CLI in `PATH`, the agent's authentication, and the workspace directory. In your own scripts, test for herdr with lib.sh's `has_real_herdr`, NOT `command -v herdr` / `type -P herdr`: once lib.sh is sourced the former matches its `herdr()` function and the latter matches the `.mam/shim` wrapper, so both pass on a host with no herdr (B-3). If a check fails, report the reason (automated path) or tell the user (interactive path). Do not proceed with a half-broken setup. ## Standard names - **herdr session name**: `derive_session_name ` (lib.sh) - `` = `basename $(dirname $WORKSPACE)` `-` `basename $WORKSPACE` (lowercase, `_`→`-`) - examples: `landing-page-refer-landing-page-creator-claude`, `paper-pdf2md-creator-agy` - never re-derive this by hand — source lib.sh and call the function - **wrapper script** (claude only): `~/.local/bin/-creator-claude` - contents: herdr new-session with `claude` inside; trust/bypass dialogs are handled by the skill for every agent (see `--no-auto-trust` below) - see `/agent_sessions.md` for the canonical wrapper template ## Herdr Session Isolation (격리 세션) When running multiple agent sessions alongside other workflows (e.g., cmux, background workers, manual herdr sessions), sharing the default herdr server can lead to session name conflicts, monitoring clutter, and accidental destruction of user sessions via global commands. To prevent this, you can run this skill inside an **isolated herdr session** using the `HERDR_SESSION_NAME` environment variable or the `--herdr-session ` flag (opt-in; alias: `--herdr-server`; legacy env alias: `HERDR_SERVER_NAME`). On success the script also starts the per-workspace monitor daemon through `start_watchdog` (`reconcile.sh --subscribe --idle-timeout 0`, one per workspace, see the lifecycle contract in [multi-agent-mux-monitor/SKILL.md](../multi-agent-mux-monitor/SKILL.md)). Set `MAM_NO_SUBSCRIBER_DAEMON=1` to skip that spawn when the monitor is supervised elsewhere or the workspace is ephemeral (Issue #19); the `watchdog PID:` line is then empty unless a daemon already runs for the workspace, whose PID is still reported. Additionally, you can specify `--herdr-workspace ` (default: workspace slug without `mam-` prefix) to name and group the agent panes inside a dedicated Herdr workspace tab in the Herdr runtime (`herdr workspace create --label` / `rename`) as well as recording it in `.mam/agent-sessions.yaml`. Note that `--herdr-workspace` configures the workspace tab label within the session, whereas `--herdr-session` selects the daemon socket itself. Under the hood this maps to a real, separate herdr **session** (`herdr --session ` — its own socket, its own `agent list`/`workspace list`, completely invisible to the default session and vice versa), not just a workspace label inside the same server. `lib.sh`'s shim bootstraps the named session's server headlessly (`herdr --session server`, backgrounded) the first time it's needed, and scopes every subsequent herdr call to it automatically — this headless bootstrap is what lets it work even when the skill itself is running from inside another herdr-managed pane (a plain interactive `herdr --session ` launch is blocked there by herdr's "nested herdr is disabled" guard; headless `server` mode isn't). The bootstrapped server receives a **sanitized copy of the caller's environment** (Issue #9): per-session agent runtime markers — `CLAUDECODE`, `CLAUDE_CODE_CHILD_SESSION`, `CLAUDE_CODE_SESSION_ID`, the Claude Code messaging/bridge/pid markers, and the `ANTIGRAVITY_CONVERSATION_ID` / `HERMES_SESSION_ID` / `GROK_SESSION_ID` / `OPENCODE_SESSION_ID` equivalents — are dropped, because herdr hands its environment to every pane it spawns and an inherited `CLAUDE_CODE_CHILD_SESSION` silently turns transcript saving off in every sub-agent claude (no `.jsonl`, so `multi-agent-mux-resume` can never find it). User configuration such as `CLAUDE_PROJECT_DIR` or `CLAUDE_CONFIG_DIR` is inherited unchanged. Query-only callers set `MAM_NO_AUTOSTART=1` to skip the bootstrap entirely (`status.sh` and `reconcile.sh` do). A server bootstrapped before this fix keeps its polluted environment until it is restarted (`herdr session stop `, then create/resume again). ### How to use 1. **Via Environment Variable**: ```bash export HERDR_SESSION_NAME=multi-agent-canary # All subsequent commands (create, status, stop, etc.) will run in the isolated 'multi-agent-canary' herdr session. ``` 2. **Via Option Flag**: ```bash bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --herdr-session multi-agent-canary --herdr-workspace my-project ``` 3. **Submit Job Integration**: You can automatically register a delegated job with a prompt when creating a session: ```bash bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --submit-job "Task prompt here" ``` 4. **Onboard Integration**: You can automatically submit a project alignment/orientation job to the new agent when creating a session: ```bash bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --onboard ``` **Delivery is judged by the agent's own `started` event** (#23), within the job's `ack_timeout_sec` (default 180 s) — not by what the pane looked like two seconds after the keys were sent. The script no longer publishes `started` on the agent's behalf, so a job that shows `started` really was picked up. A send_keys rc 4 ("Enter not seen accepted", where a still-starting TUI lands) is a warning; rc 1/2/3 still abort at once. No `started` within the ack timeout marks the job `error` and warns, but **keeps the session**: the pane is up and the row is valid, only the handshake never landed, and killing a live agent over a slow onboarding would destroy more than it protects (the same rule this script applies to a TUI that never signalled ready). Re-send with `multi-agent-mux-delegate-job submit` once the agent is up; the prompt is never re-injected automatically. `MAM_ONBOARD_ACK_S` overrides how long that wait is — distinct from the wrapper's `MAM_INJECT_ACK_GRACE_S`. After the handshake the script waits for the onboarding job to **end** (Issue #50, `MAM_ONBOARD_WAIT_S`, default 600 s) and prints `onboarding completed: job (s)`; an onboarding that ends with `error` exits 44 and one that does not end in time exits 45, both keeping the session, with `last_visible_status: onboarding-` on the row. **Start-up dialogs** are answered for every agent, not only claude (#24). The blocking-modal signatures and their accept keys live in each agent adapter (`lib_py/agents/adapters/*.py`, published as `MAM_STARTUP_DIALOGS`), and they are answered while waiting for the TUI rather than after it. `--no-auto-trust` (or `MAM_AUTO_TRUST=0` in the environment; an explicit flag wins over the environment) leaves directory-trust dialogs for a human to accept and still answers other blocking notices (for example codex's update prompt, where Skip changes nothing). Trust matters most for **codex**: trusting a directory makes it load that project's local config, hooks and exec policy, so `--no-auto-trust` is the switch to reach for when the workspace is not yours. Re-sending is rate-limited by `MAM_DIALOG_RESEND_S` (default 5 s) because a `--source visible` snapshot keeps showing a dialog for a while after it was accepted. **Exit codes**: 42 = the TUI never became ready and the process is gone; 43 = the rollback could not kill the herdr session and it is still running. On 43 the message names the session and the manual cleanup command; the YAML row keeps `status: running` with `last_visible_status: rollback-failed` and no `delegate_job_id`, and the onboarding job is closed as `cancelled`. Why use `--herdr-session`? - By default, all skills target `default` herdr session socket — fine for single-workspace use. - By using an isolated session via `HERDR_SESSION_NAME` (or `--herdr-session`), your agent sessions are completely separated from your default user workspace, ensuring 0% interference — this is backed by a genuinely separate `herdr` session/socket, not merely a workspace label. - To deliberately tear down an *entire* isolated group at once (all its workspaces and agents), use `herdr session stop ` followed by `herdr session delete ` — this only affects that named session, never the default one. - An isolated server MAM starts does not relaunch saved agents when it restarts (#45): it runs with `HERDR_CONFIG_PATH=/mam-server-config.toml`, your herdr config with `[session] resume_agents_on_restore = false`. Its saved panes come back as empty shells; resume closes the one of the session it resumes. Building that file needs `tomllib` (Python 3.11+) in the `python3` on PATH; with an older `python3` (macOS ships 3.9) restore is still off, but the file holds only that key, so the MAM server runs without your other herdr settings and the bootstrap prints a warning saying so. - That file is rebuilt each time MAM starts the server, so a change to your herdr config reaches a MAM server only when it is next started; `herdr server reload-config` rereads the generated file. A server you start yourself (`herdr session attach`) does not get it. --- ## Output format When invoked, the script creates or updates `./.mam/agent-sessions.yaml` with: ```yaml herdr_sessions: - name: -creator- # E.g. landing-page-creator-claude status: running # Initial status for a freshly created agent role: Developer herdr_session_created_at: '2026-08-04T12:00:00Z' herdr_session_epoch: 1785844800 herdr_session: # Isolated session name (default: 'mam-') delegate_job_id: null pane: index: 0 pid: 12345 cmd: claude cmd_full: claude --dangerously-skip-permissions cwd: /path/to/project start_command: "HERDR_SESSION_NAME= herdr new-session -d -s -x 140 -y 40 -c " attach_command: "HERDR_SESSION_NAME= herdr agent attach " kill_command: "HERDR_SESSION_NAME= herdr kill-session -t " ``` The three commands require `source .agents/skills/lib.sh` first — `new-session`/`kill-session` are tmux-compat pseudo-commands the shim translates, and `HERDR_SESSION_NAME` is what the shim reads to route to the right isolated herdr *session* (real `herdr` has no env-var-based scoping of its own; `herdr_session: default` needs no prefix at all). ## Workflow ```bash bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh --workspace --agent --role ``` `create_session.sh` does the whole flow: pre-flight, the session name (`derive_session_name`), the agent command (adapter `spawn-spec`), spawn, start-up dialogs, waiting for the TUI, pane capture, the YAML row, and the optional job submission. | Option | Meaning | |---|---| | `--workspace` | Required. Project directory | | `--agent` | Required. `claude`, `agy`, `hermes`, `grok`, `opencode`, `codex` | | `--role` | Required. Assigned role | | `--session` (`--name`) | herdr session name (default: `derive_session_name`) | | `--wrapper` | claude only: use the `~/.local/bin/` wrapper even if it is not present | | `--dry-run` | Print what would be spawned; no spawn and no registry (YAML/DB) update. The checks before it still run, and on macOS they remove the quarantine attribute from the agent binary | | `--herdr-session` (`--herdr-server`) | Isolated herdr server name | | `--herdr-workspace` | herdr workspace label recorded in the registry (a label only, never a socket) | | `--submit-job PROMPT` | Register a delegated job with this prompt and send it to the new agent | | `--onboard` / `--no-onboard` | Submit the onboarding job (default: on) | | `--auto-trust` / `--no-auto-trust` | Answer a start-up directory-trust dialog or leave it to a human (`MAM_AUTO_TRUST`; default: answer) | | `--isolate` / `--no-isolate` | Legacy, no-op | `MAM_ONBOARD_WAIT_S` (default 600): how long `--onboard` waits for the onboarding job to end after the `started` handshake. | Exit | Meaning | |---|---| | 0 | Created; or `--dry-run` | | 1 | Pre-flight failure, nothing was spawned: workspace not a directory, herdr not installed, agent CLI not in `PATH`, agent not authenticated. After spawn: the task brief could not be written or the instruction injection failed (the session is rolled back) | | 2 | Bad arguments; control-plane binding refused for a worktree; whitespace in a control-plane path that must be forwarded | | 3 | A herdr session of that name already exists (`multi-agent-mux-resume`, or `multi-agent-mux-stop` first) | | 4 | The YAML already has a `running` row of that name. The YAML write raises it after spawn, and the herdr session is rolled back | | 42 | The TUI never became ready and the process is gone, or it is alive and `MAM_KILL_ON_READY_TIMEOUT=1` (rolled back) | | 43 | The rollback could not kill the herdr session; it is still running | | 44 | The onboarding job ended with `error` (or was cancelled); the session is kept (Issue #50) | | 45 | The onboarding job did not end within `MAM_ONBOARD_WAIT_S`, or its record is gone; the session is kept | | other | A later step failed and its code is passed through. The EXIT trap (`cleanup_herdr_on_error`) rolls back the herdr session and closes the onboarding job as `cancelled`; if that kill fails, the exit is 43 instead | ## Registering the session in agent-sessions.yaml `create_session.sh` writes the `herdr_sessions[]` row itself (the format is in **Output format** above); do not append it by hand. `pane.cmd_full` is the actual command line in the pane, decided by the adapter `spawn-spec` (`lib_py/agents`), not the resume command. ## Issue worktrees: `worktree_prepare.sh` (Issue #21 item 4) Run from the control root's skills tree (lib.sh's `WORKSPACE_ROOT` = the main checkout that owns `.mam`): ```bash bash .agents/skills/multi-agent-mux-create/scripts/worktree_prepare.sh \ --path ../multi-agent-mux-wt/issue-42 --branch fix/issue-42 [--base ] [--submodules] \ [--creator-agent claude [--session ] [--role creator] [--herdr-session ] [--herdr-workspace ] [--onboard]] [--dry-run] ``` - Creates the worktree next to the control root (a path **inside** it is refused), idempotently; creates the worktree's **own** `.venv` from the control root's `.venv/bin/python` and installs the delegate-job skill's `requirements.txt` (paho-mqtt, PyYAML - what `deploy/install.sh` installs) plus `requirements.txt` / `requirements-dev.txt` when present (needs network access to the package index; a failed install removes the half-made venv and exits 1 so a re-run starts over; never a link - a leftover link is refused with exit 1; no control-root interpreter is a warning and `VENV=skipped`); adds `/.venv` and `.pytest_cache/` to the shared `info/exclude` (a linked worktree's exclude file is `$GIT_COMMON_DIR/info/exclude`, so it applies to the main checkout too). - **Submodules are opt-in** (`--submodules`). Initialisation writes `submodule..url` to the shared `.git/config`, and any attempt - even a failed one - leaves a `modules/` directory that makes a plain `git worktree remove` refuse. A failed `--submodules` undoes only what that attempt created - new submodule checkouts are deinitialised before their git dirs are deleted, so the worktree stays a usable checkout; submodules initialised earlier and pre-existing `submodule.*` values are kept - and exits 1; re-run without `--submodules` to finish. - `--creator-agent` creates the creator through this skill with the control root's binding and `MAM_NO_SUBSCRIBER_DAEMON=1`; the row's `pane.cwd` is the worktree. - Prints `BASE=`: record it **before** delegating the implementation, then review with `run_loop.sh --worktree --base "$BASE" --review-only`. - A submodule path that is not initialised yet but already holds files refuses `--submodules` up front (exit 3, nothing changed): the clone would fail on it and a rollback could not tell those files from its own. - Exit codes: 0 ok, 1 git / submodule / create failure, 2 usage or path rule, 3 refused to protect existing content (nothing changed); 44/45 are create's (onboarding error / timeout with `--onboard`; session and worktree kept). - create itself: a control-plane binding that cannot be computed is refused (exit 2) for a `--workspace` other than the control root and warned about for the control root; the immediate reconcile and the watchdog run the control root's code. ## Pitfalls - **Don't use `nohup`/`disown`/`setsid` for the agent itself** — those background the agent outside herdr. The whole point of this skill is *the herdr session is the supervisor*. `nohup` is OK only for *launching the wrapper* (which itself creates the herdr session via `herdr new-session -d`). - **Claude Session ID Auto-Assignment**: Fresh `claude` sessions automatically generate a new UUID (`mam_gen_uuid`) passed via `claude --session-id `. `claude_session_id_own` is stored in `.mam/agent-sessions.yaml` with `session_id_source: assigned` and `session_id_verified: false`. - **First Message Materialization**: The transcript file `.jsonl` is created on disk when the first user prompt is sent. The monitor loop (`reconcile.sh`) verifies the transcript and promotes `session_id_verified: true` and `last_visible_status: pinned`. - **Wrapper script MUST NOT be created via `hermes profile alias`** — that command writes a `hermes -p ` wrapper that destroys the herdr behavior. Create wrappers manually (see `lab-landing-page-creator-claude` template). - **Always use the workspace-relative path** in herdr `cwd` — relative paths break when herdr respawns in a different shell context. ## Verification After spawn + YAML append: ```bash # 1. herdr session is alive (real native command — no lib.sh needed) herdr agent get "$SESSION_NAME" >/dev/null 2>&1 && echo OK || echo MISSING # 2. pane has the expected cmd + cwd herdr agent get "$SESSION_NAME" | python3 -c " import sys, json a = json.load(sys.stdin)['result']['agent'] print(f\"cmd={a['agent']} cwd={a['cwd']}\") " # 3. agent-sessions.yaml has the new entry python3 -c " import yaml d = yaml.safe_load(open('.mam/agent-sessions.yaml')) names = [s['name'] for s in d['herdr_sessions']] assert '$SESSION_NAME' in names, 'session not registered' print('OK:', names) " # 4. Optional: check the TUI status (real native command) herdr agent read "$SESSION_NAME" --source visible --lines 20 # TUI ready = agent banner visible, no dialog text ``` > `herdr has-session` / `herdr list-panes` / `herdr capture-pane` are tmux-compat pseudo-commands only understood after `source .agents/skills/lib.sh` — the real `herdr` binary has no such subcommands. The block above uses only the real `herdr agent get`/`herdr agent read` commands, so it works standalone. ## When NOT to use this skill - **Resuming an old conversation** → `multi-agent-mux-resume` - **Killing an existing session** → `multi-agent-mux-stop` - **Just attaching to an existing session** → `herdr agent attach ` (no skill needed) - **One-shot print mode (claude -p "...")** → no herdr needed; use `claude-code` skill's print mode