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)**.