herdr-faq ยท diff

v0.1.0 to v0.2.0

237 added, 44 removed. Audit A to A.

---
name: herdr-faq
description: Launch and drive coding agents (codex, claude, agy) through the Herdr CLI reliably. Use before starting or prompting a subagent via herdr agent/pane commands, and when any herdr command fails or an agent seems stuck, silently lost a prompt, or reports a wrong state.
metadata:
- version: "0.1.0"
+ version: "0.2.0"
categories: "agents, operations"
topics: "herdr, troubleshooting, agent-orchestration, terminal-multiplexer"
- upstream: "herdr@0.8.2"
+ upstream: "herdr@0.9.0"
openclaw:
homepage: https://github.com/tenequm/skills/tree/main/skills/herdr-faq
emoji: "๐Ÿ‘"
---
# Herdr FAQ
- Launch and drive coding agents through [Herdr](https://herdr.dev) (>= 0.8.2) without losing prompts. Command semantics: `herdr --skill`; this file covers only what goes wrong and the recipes that avoid it.
+ Launch and drive coding agents through [Herdr](https://herdr.dev) (>= 0.9.0) without losing
+ prompts. Command semantics: `herdr --skill` (the binary ships its own current doc and is the
+ tiebreaker for any dispute with this file). This file covers only what goes wrong and the
+ recipes that avoid it.
## Invariants
- 1. **Exit 0 means queued, never delivered.** Confirm by effect: state moved, or the text is visible in the pane.
- 2. **Screens no detection rule matches read as `idle`.** Claude's trust dialog and everything agy shows can report "ready" while a dialog eats your prompt. Read the screen before the first prompt, always.
- 3. **`agent start` timeout = the child never launched** (bad flag, PATH, wrapper process). The pane has the real error; herdr's message never does.
- 4. **The driving harness is a second gate**: dangerous passthrough flags (`--dangerously-*`, `danger-full-access`) get classifier-blocked; `sleep N; herdr ...` polling is banned - use one backgrounded `prompt --wait`.
+ 1. **Exit 0 is submission, never completion.** `agent prompt` writes text and Enter as one
+ ordered submission and reports success only after both land - that still does not prove a
+ turn started. `send-keys` proves nothing at all: it can return `{"type":"ok"}` for
+ keystrokes that never reach the pane. Confirm by effect: state moved, or the text is
+ visible in the pane.
+ 2. **Screens no detection rule matches read as `idle`.** agy has no idle rule at all, so every
+ agy `idle` is a guess. Read the screen before the first prompt, always.
+ 3. **`agent start` timeout = the child never launched.** Bad flag, PATH, or a wrapper process.
+ `pane read` has the real error; `pane process-info` and herdr's own message do not.
+ 4. **`idle` is not "finished".** A claude turn that spawns background shells or a background
+ MCP task ends and reports `idle` while that work runs on. `--wait` tracks lifecycle state,
+ not turns: it can be satisfied by a turn already in flight, or by the idle an interrupted
+ turn produces a second later.
+ 5. **The driving harness is a second gate.** Dangerous passthrough flags (`--dangerously-*`,
+ `danger-full-access`) get classifier-blocked, and so can ordinary brief *content* inlined
+ into a shell command. `sleep N; herdr ...` polling is banned - use one backgrounded
+ `prompt`, or wait on a file.
## Launch
```bash
test "${HERDR_ENV:-}" = 1 # never drive herdr from outside a pane
P=$(herdr pane split --current --direction right --cwd "$PWD" --no-focus | jq -r .result.pane.pane_id)
test -n "$P" # empty $P => misleading downstream errors
herdr pane process-info --pane "$P" # must be a bare shell at its prompt
herdr agent start worker --kind codex --pane "$P" --timeout 90000 -- --approve-for-me --no-alt-screen
- # If agent_not_ready (dialog on screen): read it, answer deliberately, wait
+ # ALWAYS read before the first prompt (invariant 2), and branch on what it returns.
herdr agent read worker --source detection --lines 40
- herdr agent send-keys worker enter && herdr agent wait worker --timeout 60000
+ ```
- herdr agent read worker --source detection --lines 40 # ALWAYS before first prompt (invariant 2)
+ If that read shows a dialog, answer it deliberately - never blind-fire a key. **The safe key
+ differs per dialog**: claude's folder-trust dialog puts the cursor on `No, exit` (a bare
+ `enter` kills the agent), while its Bash-permission dialog puts it on `1. Yes`. Then:
+
+ ```bash
+ herdr agent send-keys worker down enter
+ herdr agent wait worker --until idle done --timeout 60000 # NOT a bare wait - see Drive
```
- Rules: capture every ID from JSON, never predict. Env vars via `--env KEY=VALUE` at pane/tab/workspace creation, never `pane run 'export ...'`. Names: `[a-z][a-z0-9_-]{0,31}`, namespaced (`myproj-reviewer`, never `driver`); names die with the agent - re-attach via `agent rename <pane> <name>`. `--timeout` >3000, <=300000 (default 30000). Fleets get their own workspace, `--no-focus` everywhere. After a killed `agent start`, run `herdr agent get <name>` once to free the name reservation.
+ ### Never hit the claude trust dialog again
- ### Per kind
+ The dialog appears only when the cwd has no trusted ancestor. Trust is **inherited**: a brand
+ new directory under an already-trusted parent starts clean, and claude records the child
+ automatically. Two reliable fixes:
- **codex** - `-- --approve-for-me --no-alt-screen` (never `--full-auto`: removed in 0.15x, surfaces as bare timeout). Trust dialog IS detected: untrusted dir fails fast with `agent_not_ready` - recover as above. The one-time post-`integration install` hooks-review gate is NOT detected (WONTFIX): the first pane per machine reports ready and eats its first prompt - answer it once. Session ref binds only at the first prompt: send a trivial one before relying on restore. Resume: `codex resume <id>`.
+ ```bash
+ # (a) keep agent working dirs under a tree you have already trusted once, or
+ # (b) pre-seed trust for an arbitrary path, isolated from your real config:
+ CFG=/tmp/agentcfg; mkdir -p "$CFG"
+ printf '{"projects":{"%s":{"hasTrustDialogAccepted":true}}}' "$D" > "$CFG/.claude.json"
+ herdr pane split --current --cwd "$D" --no-focus --env CLAUDE_CONFIG_DIR="$CFG"
+ ```
- **claude** (alias `claude-code`) - `-- --model <m>`. Trust dialog NOT detected: start *succeeds* on an untrusted dir and the first prompt types into the dialog - pre-trust the dir or read-then-`send-keys` (e.g. `down enter`). A Claude Code UI update can silently break detection (idle mid-turn, false `agent_prompt_stalled`): `herdr server update-agent-manifests`. A background shell in a turn hangs waits (`working` forever; fix unreleased) - use a report-file sentinel. Native-launcher installs run under a version-string process herdr can't identify (start times out, `launch_pending:true`): launch via `HERDR_AGENT=claude exec <path>`, then rename. Resume: `claude --resume <id>`.
+ Rules: capture every ID from JSON, never predict. Names `[a-z][a-z0-9_-]{0,31}`, namespaced
+ (`myproj-reviewer`, never `driver`); names die with the agent - re-attach via
+ `agent rename <pane> <name>`. Fleets get their own workspace, `--no-focus` everywhere. After a
+ killed `agent start`, run `herdr agent get <name>` once to free the name reservation
+ (reconciliation is lazy - it can take a minute or two).
- **agy** (aliases `antigravity`, `antigravity-cli`) - thinnest detection: no idle rule at all, every `idle` is a guess; the trust dialog reads idle; premature `done` mid-turn up to ~50s. Never trust a single settled state - verify by screen read or sentinel. No session ref until the first prompt. Integration install target is `antigravity-cli`; config dir `~/.gemini/config` must exist.
+ When you drive an agent under a **different `CLAUDE_CONFIG_DIR`**, it loads its own skills.
+ Confirm it has this skill current *before its first turn* - a running session has already
+ snapshotted the old text and cannot be fixed in place.
- Integrations for all three are session-restore only - they never improve state detection - and their hooks silently no-op without `python3` on PATH.
+ ## Shapes
+ The facts you need while composing a command, not after it fails.
+
+ **Output modes.** `agent get`, `agent list`, `agent start`, `agent prompt`, `agent wait`,
+ `pane split`, `pane list`, `pane process-info`, `workspace *` return **JSON** - pipe to `jq`.
+ `agent read` and `pane read` return **raw pane text** with no wrapper; piping them to `jq`
+ dies with `parse error: Invalid numeric literal` and you silently lose the read. Use `tail`,
+ `grep -qF`.
+
+ **Positional vs flagged.** `pane close` takes its id **positionally** (`herdr pane close "$P"`)
+ - the one pane command that does, while `pane split|read|list|layout|process-info` all take
+ `--pane`. `workspace create` takes `--label`, not `--name`. `send-keys` takes key names only
+ (`enter`, `esc`, `down`, `ctrl+c`); text and slash commands go through `prompt`.
+
+ **Flag dependencies and caps.** `agent start --timeout` defaults to 30000 and is capped at
+ 300000. `agent prompt --timeout` has **no cap** (1800000 is fine) but is rejected outright with
+ `--timeout requires --wait` when `--wait` is absent - and under a backgrounded call that
+ failure is invisible and the prompt is never written. Fire-and-forget sends take no flags.
+
+ **Sparse JSON.** `agent list` entries are partially populated: `name` is **absent** (not null)
+ for unnamed agents, `interactive_ready` is absent on many, and a pending start appears as
+ `{"agent_status":"unknown","launch_pending":true}` with no `agent` or `agent_session` key.
+ Null-guard every string op: `select((.name // "") | startswith("myproj-"))`.
+
+ **Env vars** go in `--env KEY=VALUE` at pane/tab/workspace creation, never
+ `pane run 'export ...'` (that sets them in a subshell the agent never sees). **Repeated flags
+ go inline** - building them in a shell variable fails, because zsh does not word-split
+ unquoted expansions and herdr receives the whole string as one argument:
+
+ ```bash
+ E="--env A=1 --env B=2"; herdr pane split ... $E # WRONG: unknown option: --env A=1 --env B=2
+ herdr pane split ... --env "A=1" --env "B=2" # right
+ ```
+
+ Bad flags print usage (some of it on **stdout**), so a `| jq` pipeline turns it into a
+ misleading parse error. Check exit status before parsing.
+
## Drive
```bash
- # One call, generous timeout, backgrounded. Never bare `agent prompt`, never prompt-then-wait.
+ # Blocking send: one call, generous timeout, backgrounded.
herdr agent prompt worker "$(cat brief.md)" --wait --timeout 1800000
- # On timeout: usually false - confirm before acting, never blind-resend
+ # Fire-and-forget note to a working agent: no flags at all.
+ herdr agent prompt worker "$(cat note.md)"
+
+ # On timeout: usually false - confirm before acting, never blind-resend.
herdr agent get worker # working = still on the turn
- # Mid-turn dialogs: wait for blocked, inspect, surface to the human, answer via send-keys
+ # Mid-turn dialogs: wait for blocked, inspect, surface to the human, answer via send-keys.
herdr agent wait worker --until blocked --timeout 120000
herdr agent read worker --source detection --lines 40
- # Output: recent-unwrapped, fall back to visible if empty (fresh panes return 0 bytes from recent)
- herdr agent read worker --source recent-unwrapped --lines 120
-
# Exit: confirm positively - shell back in foreground. Never regex the prompt.
herdr pane process-info --pane "$P" && herdr pane close "$P"
```
- - Keep `--until` at its default (`idle|done|blocked`) - narrowing it is how waits hang. A wait ending `agent_not_running` after exit/move is the event, not an error.
- - Long turns and fleets: end every brief with "write your full report to `<path>` and reply with only the path" - the file appearing is the reliable completion signal (lifecycle waits settle on transient idles), and it sidesteps alternate-screen reads.
- - Long text never goes in argv or keystrokes - the kernel tty silently truncates at 1024 bytes (macOS) / 4096 (Linux). Pass file paths.
- - `send-keys` = key names (`enter`, `esc`, `down`, `ctrl+c`); text and slash commands = `prompt`. Slash commands that open dialogs trip the fixed 5s `agent_prompt_stalled` gate - verify those by screen read, not `--wait`.
- - Queue follow-ups behind a working agent freely; `agent prompt` refuses blocked agents (`agent_blocked`) before writing anything.
- - Gate decisions on `agent_status`, never `interactive_ready` (stays true while blocked).
+ - **Wait on the artifact, not the state.** End every non-trivial brief with "write your full
+ report to `<path>` and reply with only the path", and poll for the file. `idle` arrives on
+ interrupted turns, on fleet parents that dispatched children and ended their turn, and for
+ the entire multi-minute life of a claude background MCP task. A watcher written as
+ `[ "$s" != "working" ] && echo stopped` false-alarms on every one of those.
+ - `--until` defaults to `idle|done|blocked`; keep it there for turn waits, because narrowing
+ it is how waits hang. **One exception**: right after answering a dialog, a bare `wait`
+ returns the still-`blocked` state within milliseconds - a no-op that looks like the key
+ never landed. Gate on `--until idle done` there, or poll `agent get`. A wait ending
+ `agent_not_running` after the agent exited or moved is the event, not an error.
+ - `done` and `idle` both mean ready for input; `done` is an unseen completion, and focusing the
+ tab silently rewrites it to `idle`. Each TUI client tracks this independently, so its badge
+ can disagree with the CLI. Gate decisions on `agent_status`, never `interactive_ready`
+ (which stays true while blocked).
+ - Long text is safe through `agent prompt` - it uses bracketed paste, and 8 KB arrives intact.
+ The 1024-byte (macOS) tty truncation is real but applies to **`pane send-text`** and
+ keystrokes. Pass file paths anyway when the text is a brief: it also keeps the content out
+ of the shell command, where the driving harness's classifier can block on it.
+ - Output: `--source recent-unwrapped` for transcript, `detection` for the last screen,
+ `visible` for what is on screen now. Grey "next prompt suggestion" text in a claude composer
+ reads as real unsubmitted text in a plain read - disambiguate with `--format ansi` (the
+ suggestion carries the dim attribute `ESC[2m`).
+ - Queue follow-ups behind a working agent freely; `agent prompt` refuses blocked agents
+ (`agent_blocked`) before writing anything. But do not attach `--wait` to a queued follow-up:
+ it can be satisfied by the turn already running.
+ - Slash commands that open dialogs trip the fixed 5s `agent_prompt_stalled` gate - verify
+ those by screen read, not `--wait`.
## Failures
Triage first:
- - Any start failure -> `herdr pane process-info --pane <id>`: foreign foreground process = busy/race (below); only the shell yet it timed out = profile still loading or an invisible nested shell (Windows); agent under a wrapper (`node`, a bare version string) = herdr can't identify it -> relaunch via `HERDR_AGENT=<kind> exec <cmd>` (or `exec -a <kind>`), then `agent rename`.
- - Any wrong state -> `herdr agent explain <target> --verbose` + `agent read --source detection`. Matched rule null + `default_known_agent_idle_fallback` = herdr is guessing. Stale manifest -> `herdr server update-agent-manifests` (no restart needed). Local rule patch: `~/.config/herdr/agent-detection/<kind>.toml`.
- - Logs: `~/.config/herdr/herdr-server.log`, `HERDR_LOG=herdr=debug`. After a binary update: `herdr server stop` + relaunch (old server keeps serving; stop kills pane processes) and `herdr integration status --outdated-only`.
+ - **Any start failure** -> `herdr pane read <pane> --source visible` FIRST. The most common
+ cause is a bad flag: the child launched, printed `error: unknown option ...`, and exited,
+ leaving only the shell - so `process-info` looks innocent and herdr reports a bare
+ `timeout`. Then `pane process-info --pane <id>`: a foreign foreground process = busy/race;
+ an agent under a wrapper (`node`, a bare version string) = herdr cannot identify it ->
+ relaunch via `HERDR_AGENT=<kind> exec <cmd>`, then `agent rename`. An agent under
+ `docker exec`, `podman exec`, or `ssh -t` is permanently undetectable (WONTFIX).
+ - **Any wrong state** -> `herdr agent explain <target> --verbose` + `agent read --source
+ detection`. Matched rule null + `default_known_agent_idle_fallback` = herdr is guessing.
+ Stale manifest -> `herdr server update-agent-manifests` (no restart; named servers now pick
+ up each other's downloads). Local rule patch: `~/.config/herdr/agent-detection/<kind>.toml`.
+ - **Logs**: `~/.config/herdr/herdr-server.log`, `HERDR_LOG=herdr=debug`.
+ - **After a binary update**, do NOT reflexively `server stop`. 0.9.0 leaves a compatible server
+ and its running agents untouched; check `herdr status` (`endpoint_compatible`,
+ `restart_needed`, `server_binary_stale`) and stop only for server-side changes - stopping
+ kills every pane process and in-flight turn, unresumably. A **stale client** inverts the
+ advice: it reports the running server as "old" while `pane list` correctly says
+ `client protocol N is older than server`. Trust `herdr status`.
+ - **"herdr is stuck"** is often the terminal emulator, not herdr. A wedged surface takes no
+ input while panes and agents are fine in the server. Open a fresh tab before touching the
+ server.
- **`agent_not_ready` (start)** - a dialog is on screen. Exit 1 but the agent is running and the name is bound (documented contract). Read, answer via send-keys, wait, prompt. A blocked launch never times out: `launch_pending` stays true and `rename` returns `agent_launch_pending` until the dialog is answered or the process exits. On `agent prompt` the same code means launch pending or the agent left the foreground.
+ **`agent_not_ready` (start)** - a dialog is on screen. Exit 1 but the agent is running and the
+ name is bound (documented contract). Read, answer via send-keys, wait on `--until idle done`,
+ prompt. A blocked launch never times out: `launch_pending` stays true and `rename` returns
+ `agent_launch_pending` until the dialog is answered or the process exits. Both codex's
+ trust-directory dialog and its startup update dialog are detected; claude's folder-trust
+ dialog is caught too, by the generic blocked-form rule. Codex's one-time post-`integration
+ install` hooks-review gate is NOT detected (WONTFIX) - answer it once per machine.
- **`timeout` (start)** - invariant 3; read the pane. `command not found` in a non-login shell: set `[terminal] shell_mode = "login"`, recreate the pane. Windows npm-shim installs fail only when `--` args are passed: `pane run "<cmd with flags>"`, wait, `agent rename`.
+ **`timeout` (start)** - invariant 3; read the pane. `command not found` in a non-login shell:
+ set `[terminal] shell_mode = "login"`, recreate the pane. Under `--remote`, panes inherit the
+ *server's* PATH, so a `~/.local/bin` agent CLI is not found and you get a bare timeout.
- **`agent_name_taken` / `agent_launch_pending`** - reservations are made before launch and reconciled lazily: `herdr agent get <name>` once frees an expired one. Still wedged (rename -> pending, get -> not_found, restart -> pane_busy): burn both - fresh pane, fresh name. Names are cross-workspace and freed names get recycled - namespace them.
+ **`agent_name_taken` / `agent_launch_pending`** - reservations are made before launch and
+ reconciled lazily: `herdr agent get <name>` once frees an expired one. Still wedged (rename ->
+ pending, get -> not_found, restart -> pane_busy): burn both - fresh pane, fresh name. Names are
+ cross-workspace and freed names get recycled - namespace them.
- **`agent_pane_busy`** - "available shell" = the shell itself, alone, in the foreground. Three cases: (a) racy - shell still running its rc files (`starship`, `direnv` etc.); herdr retries only 2s (never on Windows) and `pane get` looks identical ready vs not, so retry with backoff and clean up the tab a failed attempt orphaned; (b) genuine occupant - split a new pane, don't reclaim (killing the occupant cascades into `pane_not_found`); (c) permanent on Windows - a profile that chain-launches pwsh nests shells invisibly: `[terminal] default_shell = "pwsh.exe"`, reload config, recreate panes.
+ **`agent_pane_busy`** - "available shell" = the shell itself, alone, in the foreground. Three
+ cases: (a) racy - shell still running its rc files (`starship`, `direnv`); herdr retries only
+ 2s (never on Windows) and `pane get` looks identical ready vs not, so retry with backoff and
+ clean up any orphaned tab; (b) genuine occupant - split a new pane, do not reclaim (killing the
+ occupant cascades into `pane_not_found`); (c) permanent on Windows - a profile that
+ chain-launches pwsh nests shells invisibly: `[terminal] default_shell = "pwsh.exe"`.
- **`agent_not_found`** - downstream symptom: failed start, exited agent, or a bad name earlier in the loop. A pane holding only a shell also returns it (not `pane_not_found`). A live pane can rarely lose registration while the TUI runs fine - `agent rename <pane> <same-name>` restores it.
+ **`agent_not_found`** - downstream symptom: failed start, exited agent, or a bad name earlier
+ in the loop. A pane holding only a shell also returns it. A live pane can rarely lose
+ registration while the TUI runs fine - `agent rename <pane> <same-name>` restores it.
- **`timeout` (prompt --wait / wait)** - usually the turn outlasted the timeout: `agent get` shows `working`. Use 1800000+, background it, never resend on timeout alone. `--timeout <= 5000` reports this code instead of `agent_prompt_stalled`.
+ **`timeout` (prompt --wait / wait)** - usually the turn outlasted the timeout: `agent get`
+ shows `working`. Use 1800000+, background it, never resend on timeout alone.
- **`agent_prompt_stalled`** - the 5s gate is fixed. Causes in observed order: stale manifest; dialog-opening prompt; a target-side paste modal swallowing Enter (omp's Large Paste Menu - disable it in omp `/settings`, or pass a file path); Windows input races on long prompts. Never blind-resend and never recover with a lone `send-keys enter` (it can silently no-op). Read the pane: text sitting in the composer -> one `enter`; text absent -> re-prompt. Verify pattern:
+ **`agent_prompt_stalled`** - the 5s gate is fixed and now starts *after* submission, accepting
+ only observed `working` or `blocked`; the caller timeout includes submission time, and expiring
+ first returns `timeout` instead. **It is not proof of non-delivery** - the text may have landed
+ and been consumed. Causes in observed order: stale manifest; dialog-opening prompt; a
+ target-side paste modal swallowing Enter (omp's Large Paste Menu, which triggers on *line
+ count*, not bytes - disable it in omp `/settings`); Windows input races on long prompts. Never
+ blind-resend and never recover with a lone `send-keys enter` (it can silently no-op). Read the
+ pane, then re-send with a fresh `agent prompt` - not a keystroke:
```bash
for i in 1 2 3; do
herdr agent prompt "$A" "$TEXT" --wait --timeout 60000 && break
herdr agent read "$A" --source recent-unwrapped --lines 200 | grep -qF "${TEXT:0:80}" && break
done
```
- **`agent_blocked` (prompt)** - refused before anything is written. Read detection, surface the dialog, answer via send-keys. Stale scrollback `[y/n]` text can also classify codex as blocked - update manifests.
+ **`agent_blocked` (prompt)** - refused before anything is written. Read detection, surface the
+ dialog, answer via send-keys.
- **`invalid_agent_name`** - grammar above; shell loops producing uppercase are the classic cause, and one bad name cascades into a wall of `agent_not_found`s.
+ **`invalid_agent_name`** - grammar above; shell loops producing uppercase are the classic
+ cause, and one bad name cascades into a wall of `agent_not_found`s.
- **`pane_not_found` / `workspace_not_found` / `unknown option: <valid-looking value>`** - IDs are runtime-only, never reused: re-list (`workspace list`, `agent list`) at session start, recreate only what's missing. Closing the last tab closes its workspace. An empty `$P` makes the CLI parser blame the wrong token.
+ **`pane_not_found` / `workspace_not_found` / `unknown option: <valid-looking value>`** - IDs are
+ runtime-only, never reused: re-list at session start, recreate only what is missing.
+ `unknown option` on a whole flag string is the shell-variable trap (see Shapes); on a lone
+ token it is usually an empty `$P` making the parser blame the wrong thing.
- **`invalid_key` / flag errors** - `send-keys` takes key names only. `workspace create` takes `--label`, not `--name`. Bad flags print usage on stderr and exit 2, so `herdr ... | jq` dies with a misleading parse error - check exit status before parsing.
+ **`workspace_group_close_required`** - closing a primary workspace with open worktree
+ workspaces needs `workspace close --group`. Closing the last tab closes its workspace. `tab close` can likewise return
+ `confirmation_required` when it would close a whole worktree group.
- **Harness blocks** - classifier denial on dangerous passthrough flags: put permissiveness in the child agent's own config ("Stage 2 classifier error" is transient - retry once). Allowlist read-only commands (`agent get/read/list/wait/explain`, `pane read/list/process-info`, `workspace list`) or every call prompts.
+ **Harness blocks** - classifier denial on dangerous passthrough flags: put permissiveness in
+ the child agent's own config ("Stage 2 classifier error" is transient - retry once). Allowlist
+ read-only commands (`agent get/read/list/wait/explain`, `pane read/list/process-info`,
+ `workspace list`) or every call prompts.
- Silent failures (exit 0, no error): fallback-idle prompt swallowing (invariant 2); bare `agent prompt` leaving text unsubmitted in an out-of-view pane; tty truncation of long lines; a first turn going straight `unknown -> working -> idle` skips `done` and its notification; detection sees only the last ~24 rows, so a tall dialog in a short pane is partly invisible; `pane wait-output` matches the echoed command itself - never use it for readiness.
+ Silent failures (exit 0, no error): fallback-idle prompt swallowing (invariant 2); `send-keys`
+ returning ok without delivering; bare `agent prompt` leaving text unsubmitted in an
+ out-of-view pane; tty truncation of long `send-text`; a first turn going straight
+ `unknown -> working -> idle` skips `done` and its notification; detection sees only the pane's
+ own rows (fallback 24), so a tall dialog in a short pane is partly invisible;
+ `pane wait-output` matches the echoed command itself - never use it for readiness;
+ `agent focus` can return ok without moving the viewport in 0.9.0 - use `tab focus` instead.
+
+ ## Per kind
+
+ **codex** - `-- --approve-for-me --no-alt-screen` (never `--full-auto`: removed in 0.15x,
+ surfaces as a bare timeout and can leave the pane wedged in startup-pending). Trust dialog and
+ startup update dialog are both detected; an untrusted dir fails fast with `agent_not_ready`.
+ Session ref binds at the first prompt for a fresh start, but an explicit `-- resume <id>` is
+ persisted at launch.
+
+ **claude** (alias `claude-code`) - `-- --model <m>`. The folder-trust dialog **is** detected
+ (`agent_not_ready`), so pre-trust the dir (see Launch) rather than recovering. Session ref
+ binds at launch. A turn with background shells ends and reports `idle` while they run;
+ background *agents* and background *MCP tasks* keep it `working`, and a background MCP task can
+ hold `idle` for its whole 3-10 minute life. A Claude Code UI update can still break detection
+ (`herdr server update-agent-manifests`). Native-launcher installs run under a version-string
+ process herdr cannot identify: launch via `HERDR_AGENT=claude exec <path>`, then rename.
+
+ **agy** (aliases `antigravity`, `antigravity-cli`) - thinnest detection: no idle rule at all,
+ the trust dialog reads idle, and premature `done` mid-turn lasts up to ~50s while the pane
+ visibly streams (grok shares this bug). Never trust a single settled state - verify by screen
+ read or sentinel. A first prompt can be swallowed entirely with `agent_prompt_stalled` and no
+ trace in the composer; re-prompting works. No session ref until the first prompt. Integration
+ install target is `antigravity-cli`; config dir `~/.gemini/config` must exist (or
+ `ANTIGRAVITY_CLI_CONFIG_DIR`).
+
+ Integrations for all three are session-restore only - they never improve state detection - and
+ their hooks silently no-op without `python3` on PATH. `integration status` cannot see a
+ codex-side disabled hook (WONTFIX); verify `agent_session` is present after the first codex
+ turn instead.
+
+ ## Multiple machines (0.9.0)
+
+ `herdr machine` manages saved SSH endpoints, but it is a **TUI-only** surface: there is no
+ machine namespace in the CLI or socket API. IDs and live agent names are scoped to one server,
+ two machines can both hold `w1:p1` or an agent named `reviewer`, and selecting a machine in the
+ TUI does **not** retarget CLI commands run in your pane - they still use the inherited socket.
+ For automation, run the herdr CLI on the intended host over ssh and rediscover IDs there. Only
+ add, remove, enable, or disable profiles when the user asks; setup asks before stopping an
+ incompatible remote server and defaults to No - do not approve replacement without consent.
+ `--trust-repository` on `worktree` grants per-request Git trust and is not a routine retry.