v1.11.0

umputun/ralphexv1.11.0Aug 1, 2026by github-actions[bot]

AI Summary

A security and correctness release featuring a breaking change to `session send` exit codes and hardening worktree scripts and session identity verification.

Key Highlights

  • **Breaking Change**: `session send` exit codes now non-zero on typed/failed
  • Worktree scripts now require explicit consent
  • Skills-catalog path containment and security
  • Session identity verified before resume
  • Usage-limit bounces detected from transcript

Breaking Changes

  • `session send` now exits non-zero when a message reached the pane but was never confirmed submitted

New Features

  • Worktree consent
  • Path containment security
  • Session resume verification
  • Usage-limit detection
  • Hermes status reporting

Full Release Notes

# v1.11.0

Security and correctness release. Repo-supplied worktree scripts now require
explicit consent, path handling around the skills catalog and logs is hardened,
`session send` tells the truth about delivery, restarted sessions can no longer
resume the wrong conversation, and Ctrl+Q detach works on every session sharing
a tmux socket.

## ⚠️ Behaviour change: `agent-deck session send` exit codes

**`session send` now exits non-zero when a message reached the pane but was
never confirmed submitted** ([#1831](https://github.com/asheshgoplani/agent-deck/pull/1831),
closes [#1793](https://github.com/asheshgoplani/agent-deck/issues/1793)).
Previously these cases exited 0, so a script that treated exit 0 as "delivered"
could silently drop work at an agent's composer.

| delivery | exit | `success` | `submitted` |
|---|---|---|---|
| `submitted` | 0 | true | true |
| `typed` | **1** | false | false |
| `typed_not_submitted` | **1** | false | false |
| `line_too_long` | **1** | false | false |
| `no_evidence` | **1** | false | false |
| `send_failed` | **1** | false | false |

`--json` gains a machine-checkable `submitted` boolean so callers never have to
know which delivery strings imply an accepted turn — only `submitted` does.
Failures carry `code: delivery_failed`.

**Action required:** if you have automation that calls `session send` and only
checks the exit code, it will now correctly fail on sends that previously looked
successful. Retry on `delivery: typed` / `typed_not_submitted`; treat
`line_too_long` as non-retryable (break the line or send a file reference).

## Security

- **Worktree setup and destruction scripts now require explicit consent before
  running** ([#1810](https://github.com/asheshgoplani/agent-deck/pull/1810)). A
  repo's `.agent-deck/worktree-setup.sh` runs with your full environment, so
  cloning a repo and creating a worktree no longer executes it unprompted. New
  `[worktree] run_repo_scripts`: `"prompt"` (default — approve once per repo
  root and script content, re-prompting if the content changes), `"always"`
  (previous behaviour, opt-in), `"never"`. Unknown values fail closed to
  `"prompt"`. Non-interactive callers (CI, the remote worktree-mutation
  endpoint, the TUI's raw-mode terminal) fail closed with a remediation message
  instead of hanging. New `agent-deck worktree trust-scripts [--revoke]`, plus
  `--allow-repo-scripts` / `AGENT_DECK_ALLOW_REPO_SCRIPTS=1` for a one-shot,
  never-persisted CI bypass.
- **Skills-catalog path containment**
  ([#1809](https://github.com/asheshgoplani/agent-deck/pull/1809),
  [#1811](https://github.com/asheshgoplani/agent-deck/pull/1811)). Managed skill
  targets are now resolved through descriptor-pinned `os.Root` walks and
  re-verified by inode identity, so a path component that changes between check
  and use is refused rather than followed. Symlinked ancestors, cross-filesystem
  violations, and mount points planted under a managed directory are refused;
  removal walks the tree itself instead of `RemoveAll`, unlinking symlinks
  without following them. The legitimate pool-attach pattern (final component is
  a symlink into the skills pool) is unchanged and pinned by a regression test.
- **Log sanitizer barrier**
  ([#1809](https://github.com/asheshgoplani/agent-deck/pull/1809),
  [#1812](https://github.com/asheshgoplani/agent-deck/pull/1812)).
  `logging.SanitizeValue` now rebuilds the string on every path; the old
  "nothing to escape" fast path returned its input unchanged. Non-UTF-8 input
  now becomes U+FFFD rather than surviving intact — deliberate, and pinned by a
  test.
- **Ctrl+Q detach binding no longer builds a shell command from tmux-supplied
  values** ([#1820](https://github.com/asheshgoplani/agent-deck/pull/1820)). The
  socket-wide binding is now a pure tmux-native
  `if-shell -F "#{m:...}" detach-client`, evaluated entirely inside tmux's
  format engine, with no `/bin/sh` in the path. The same change is applied to
  `BindMouseStatusRightDetach`.
- **Remote worktree creation is refused**
  ([#1712](https://github.com/asheshgoplani/agent-deck/pull/1712)) — thanks
  **[@kewtyboi](https://github.com/kewtyboi)**. `add --ssh <host> -w -b` no
  longer half-creates a local worktree for a remote session; it refuses with
  instructions. A bare positional path alongside `--ssh` now routes to
  `--remote-path` (and must be absolute) instead of being silently misused as a
  local project path.

## Session identity & profiles

- **Conversation identity is verified before any `--resume`**
  ([#1830](https://github.com/asheshgoplani/agent-deck/pull/1830), closes
  [#1815](https://github.com/asheshgoplani/agent-deck/issues/1815)). A restarted
  child session could resume a *different* session's conversation — observed
  live, with a restarted child picking up its parent conductor's transcript.
  agent-deck now proves the transcript it is about to resume belongs to that
  session before passing `--resume`, and declines to resume rather than
  attaching to someone else's conversation.
- **Unknown `CLAUDE_CONFIG_DIR`-inferred profiles warn and fall back**
  ([#1822](https://github.com/asheshgoplani/agent-deck/pull/1822), closes
  [#1790](https://github.com/asheshgoplani/agent-deck/issues/1790) and
  [#1791](https://github.com/asheshgoplani/agent-deck/issues/1791)). A config
  directory that maps to a profile agent-deck does not know about no longer
  silently selects a wrong profile; it warns and falls back. `CLAUDE_CONFIG_DIR`
  is also exported host-side so the value the session runs with matches the one
  agent-deck resolved.

## Status truth

- **Usage-limit bounces are detected from the transcript, not pane text**
  ([#1806](https://github.com/asheshgoplani/agent-deck/pull/1806), closes
  [#1802](https://github.com/asheshgoplani/agent-deck/issues/1802)) — thanks
  **[@AndreIntelas](https://github.com/AndreIntelas)**. New usage-limit
  substate; pane-scraping false positives are gone.
- **Working-directory guard**
  ([#1769](https://github.com/asheshgoplani/agent-deck/pull/1769)). A start
  whose pane cannot reach its working directory is now refused. tmux silently
  starts the pane in `$HOME` when the directory is gone, so the agent would
  otherwise run against the wrong tree.
- **Watchdog confirms liveness before restarting**
  ([#1768](https://github.com/asheshgoplani/agent-deck/pull/1768)) — no more
  restarting a session that was merely quiet.
- **Auth-hold sidecar**
  ([#1751](https://github.com/asheshgoplani/agent-deck/pull/1751)) — private
  sidecar plus a staleness check on death adoption, so auth-dead sessions are
  held rather than flapped.
- **Read-only `status --stale` candidate view**
  ([#1704](https://github.com/asheshgoplani/agent-deck/issues/1704)) — thanks
  **[@kewtyboi](https://github.com/kewtyboi)** for the request.
- **Absolute `project_path` on every local write path**
  ([#1770](https://github.com/asheshgoplani/agent-deck/pull/1770),
  [#1766](https://github.com/asheshgoplani/agent-deck/pull/1766)).
- **`launch -t` title is locked** against agent name sync
  ([#1750](https://github.com/asheshgoplani/agent-deck/pull/1750)).
- **Hermes status reporting works**
  ([#1807](https://github.com/asheshgoplani/agent-deck/pull/1807)) — thanks
  **[@drmzperx](https://github.com/drmzperx)**. Env injection, turn-level hooks,
  and corrected turn-end mapping.

## Attach & tmux

- **Ctrl+Q detach is scoped per session, not per socket**
  ([#1820](https://github.com/asheshgoplani/agent-deck/pull/1820), closes
  [#1808](https://github.com/asheshgoplani/agent-deck/issues/1808)). Key
  bindings live on the tmux *server*, and the old guard baked in whichever
  session started most recently — so on a shared socket every other session
  silently swallowed Ctrl+Q. Now matched against the invoking client's own
  session at keypress time.
- **`-u` on the remaining attach paths**
  ([#1789](https://github.com/asheshgoplani/agent-deck/pull/1789)) — thanks
  **[@Jonesxq](https://github.com/Jonesxq)** — and **UTF-8 forced on the
  terminal-bridge attach**
  ([#1779](https://github.com/asheshgoplani/agent-deck/pull/1779)) — thanks
  **[@hfreire](https://github.com/hfreire)**.
- **`inject_status_line = false` is honored** on the watcher/reconnect path
  ([#1780](https://github.com/asheshgoplani/agent-deck/pull/1780)) — thanks
  **[@hfreire](https://github.com/hfreire)**.
- **Detached sessions are born at the real terminal size**
  ([#1752](https://github.com/asheshgoplani/agent-deck/pull/1752)).
- **Per-session ownership is proven before stopping a service-mode unit**
  ([#1754](https://github.com/asheshgoplani/agent-deck/pull/1754)).
- **Attach return guards strengthened**
  ([#1787](https://github.com/asheshgoplani/agent-deck/pull/1787)) — thanks
  **[@terminalchai](https://github.com/terminalchai)**.

## Features

- **`[interval_hooks]`**
  ([#1628](https://github.com/asheshgoplani/agent-deck/pull/1628)) — thanks
  **[@jdidion](https://github.com/jdidion)**. Run shell commands on a wall-clock
  cadence while the TUI is running, independent of session activity: a periodic
  sync, a health probe, or a poll that dispatches work to sessions. Configured
  only in your own `~/.agent-deck/config.toml`.
- **Opt-in sparse-checkout inheritance for new worktrees**
  ([#1757](https://github.com/asheshgoplani/agent-deck/pull/1757), closes
  [#1708](https://github.com/asheshgoplani/agent-deck/issues/1708)).
  `[worktree] sparse_checkout = "inherit"` captures the source worktree's mode
  and patterns and creates with `--no-checkout`, so a sparse monorepo never
  materializes the full tree first.

## Performance & rendering

- **No more black screen on the first frame after detach**
  ([#1774](https://github.com/asheshgoplani/agent-deck/pull/1774), closes
  [#1753](https://github.com/asheshgoplani/agent-deck/issues/1753)). Returning
  from an attached session could paint an empty frame, and the redraw cost
  scaled with the number of visible rows. The first frame after detach is now
  never empty and no longer O(visible rows).
- **Frames are only rebuilt for width when they actually overflow**
  ([#1756](https://github.com/asheshgoplani/agent-deck/pull/1756)).
- **Detach/switch returns instantly at large fleet sizes**
  ([#1764](https://github.com/asheshgoplani/agent-deck/pull/1764)).

## Docs

- README session naming — title-lock, `sync_title`, groups
  ([#1823](https://github.com/asheshgoplani/agent-deck/pull/1823),
  [#1828](https://github.com/asheshgoplani/agent-deck/pull/1828)).
- Skill docs: `agent-deck try` scratch sessions
  ([#1798](https://github.com/asheshgoplani/agent-deck/pull/1798)), fleet
  cross-reference and `session children`
  ([#1796](https://github.com/asheshgoplani/agent-deck/pull/1796)), the
  session-manager vs process-supervisor boundary
  ([#1797](https://github.com/asheshgoplani/agent-deck/pull/1797)), and the
  claude-subcommand flag-injection gotcha
  ([#1801](https://github.com/asheshgoplani/agent-deck/pull/1801)) — all thanks
  **[@tarekrached](https://github.com/tarekrached)**.
- Substate guidance in the shared conductor CLAUDE.md template
  ([#1814](https://github.com/asheshgoplani/agent-deck/pull/1814)).

## Release & test hygiene

- Releases publish only after assets are verified
  ([#1762](https://github.com/asheshgoplani/agent-deck/pull/1762), closes
  [#1759](https://github.com/asheshgoplani/agent-deck/issues/1759)), and the
  brew tap push is out of the critical path so SLSA provenance always ships
  ([#1767](https://github.com/asheshgoplani/agent-deck/pull/1767), closes
  [#1763](https://github.com/asheshgoplani/agent-deck/issues/1763),
  [#1760](https://github.com/asheshgoplani/agent-deck/issues/1760)).
- Stale-tmux reaper covers eval binaries and wider socket families, with a
  DRY_RUN smoke test and a fixed launchd deployment
  ([#1749](https://github.com/asheshgoplani/agent-deck/pull/1749)).
- Deflaked control-pipe, cgroup-decision, and persistence-discovery tests
  ([#1812](https://github.com/asheshgoplani/agent-deck/pull/1812)); made three
  `internal/session` tests host-independent on macOS
  ([#1755](https://github.com/asheshgoplani/agent-deck/pull/1755), closes
  [#1720](https://github.com/asheshgoplani/agent-deck/issues/1720));
  `TestCanRestartCursor` no longer requires the cursor CLI
  ([#1805](https://github.com/asheshgoplani/agent-deck/pull/1805)) — thanks
  **[@AndreIntelas](https://github.com/AndreIntelas)**.
- Dependency bumps
  ([#1785](https://github.com/asheshgoplani/agent-deck/pull/1785)).

## Contributors

Thanks to **[@AndreIntelas](https://github.com/AndreIntelas)**,
**[@drmzperx](https://github.com/drmzperx)**,
**[@hfreire](https://github.com/hfreire)**,
**[@jdidion](https://github.com/jdidion)**,
**[@Jonesxq](https://github.com/Jonesxq)**,
**[@kewtyboi](https://github.com/kewtyboi)**,
**[@tarekrached](https://github.com/tarekrached)**, and
**[@terminalchai](https://github.com/terminalchai)**.