A hand-authored agent whose command launches a wrapper — a shim script, a Nix/env-wrapped binary, anything whose program basename is not literally claude — silently never receives dings. There is no way to say otherwise in the spec, and nothing anywhere reports it.
Verified at 4e25f56. This is our hard blocker for cutting a live seat over to the native bus, so apologies for the length — but the first half of it is clearly your deliberate design, and we want to be precise about which part we think is a gap.
What is intentional (we are not asking you to change this)
DeliveryMode::for_agent_command selects the legacy text+Return path only for a positively identified claude program name, and guards everything else:
src/ding.rs:150-156 → command_invokes(command, "claude")
src/ding.rs:159-170 — basename comparison after stripping a leading exec
src/main.rs:1083-1104 — ding_delivery_mode pulls the agent's pty task command and feeds it in
Your doc comments say exactly this — "Unknown or unrendered agent commands default to the fail-closed Codex guard" (src/ding.rs:137-139), "A missing, wrapped, or unfamiliar command remains guarded" (src/ding.rs:149) — and src/ding.rs:2408-2412 tests sh -c 'claude' → CodexGuarded on purpose. Fail-closed is the right default and we are not proposing to weaken it.
The gap: there is no lever, and the closed state is invisible
1. A spec cannot declare its delivery mode. harness is one of the render-only fields the KDL parser explicitly drops (src/kdl_format.rs:6, src/kdl_format.rs:84). So delivery mode is derived only from the emitted command string, and an author whose command is a wrapper, a shim, an absolute path under a package manager's store, or env-prefixed has no way to say "this pane is a Claude TUI". The declaration is correct-looking and undeliverable. That sits awkwardly with hand-authored KDL being the canonical interface.
2. A guarded deferral that can never resolve produces no output. CodexGuarded inspects the pane for an idle Codex composer; against a Claude TUI it never finds one, so every attempt returns Deferred and flush_pending breaks out silently — src/ding.rs:1188 — with no log, no counter, and no state change. Meanwhile the ding process is alive, so its presence refresh keeps writing available, its task passes st2 doctor, and its own log ends at its two startup lines. Through every surface st2 exposes, a permanently undeliverable agent is identical to an idle one.
Observation
Isolated with an agent-free control: two panes running only sleep 900, identical in every respect except the program basename of the emitted command.
- basename
claude → Legacy → 1 DING line delivered
- basename anything else (ours is a wrapper that
execs claude) → CodexGuarded → 0 DING lines, indefinitely
Impact for an adopter
Any site that does not invoke the harness binary by its bare name is silently unreachable on the native bus, and the failure is not observable from the spec, the sidecar log, presence, or doctor. We only found it by diffing two otherwise-identical panes.
Offer — your call on shape
Two things, and we would send them as separate PRs so you can take either independently:
- A spec-declarable delivery mode, with
for_agent_command unchanged as the fallback when it is absent. Additive, does not alter any existing behaviour or test.
- Make the guarded path observable — a deferral that never resolves should say so somewhere, rather than looking idle. Whether that belongs in the sidecar log, in
doctor, or in presence is a design decision we would rather you made; a log line is the smallest version.
Both would come with regression tests. We may well be duplicating something you already have unpushed — happy for these to be closed in favour of your version, and we would rather you reshaped them than merged ours as-is if the shape is wrong.
A hand-authored agent whose
commandlaunches a wrapper — a shim script, a Nix/env-wrapped binary, anything whose program basename is not literallyclaude— silently never receives dings. There is no way to say otherwise in the spec, and nothing anywhere reports it.Verified at
4e25f56. This is our hard blocker for cutting a live seat over to the native bus, so apologies for the length — but the first half of it is clearly your deliberate design, and we want to be precise about which part we think is a gap.What is intentional (we are not asking you to change this)
DeliveryMode::for_agent_commandselects the legacy text+Return path only for a positively identifiedclaudeprogram name, and guards everything else:src/ding.rs:150-156→command_invokes(command, "claude")src/ding.rs:159-170— basename comparison after stripping a leadingexecsrc/main.rs:1083-1104—ding_delivery_modepulls the agent's pty task command and feeds it inYour doc comments say exactly this — "Unknown or unrendered agent commands default to the fail-closed Codex guard" (
src/ding.rs:137-139), "A missing, wrapped, or unfamiliar command remains guarded" (src/ding.rs:149) — andsrc/ding.rs:2408-2412testssh -c 'claude'→CodexGuardedon purpose. Fail-closed is the right default and we are not proposing to weaken it.The gap: there is no lever, and the closed state is invisible
1. A spec cannot declare its delivery mode.
harnessis one of the render-only fields the KDL parser explicitly drops (src/kdl_format.rs:6,src/kdl_format.rs:84). So delivery mode is derived only from the emitted command string, and an author whose command is a wrapper, a shim, an absolute path under a package manager's store, orenv-prefixed has no way to say "this pane is a Claude TUI". The declaration is correct-looking and undeliverable. That sits awkwardly with hand-authored KDL being the canonical interface.2. A guarded deferral that can never resolve produces no output.
CodexGuardedinspects the pane for an idle Codex composer; against a Claude TUI it never finds one, so every attempt returnsDeferredandflush_pendingbreaks out silently —src/ding.rs:1188— with no log, no counter, and no state change. Meanwhile the ding process is alive, so its presence refresh keeps writingavailable, its task passesst2 doctor, and its own log ends at its two startup lines. Through every surface st2 exposes, a permanently undeliverable agent is identical to an idle one.Observation
Isolated with an agent-free control: two panes running only
sleep 900, identical in every respect except the program basename of the emittedcommand.claude→Legacy→ 1 DING line deliveredexecs claude) →CodexGuarded→ 0 DING lines, indefinitelyImpact for an adopter
Any site that does not invoke the harness binary by its bare name is silently unreachable on the native bus, and the failure is not observable from the spec, the sidecar log, presence, or doctor. We only found it by diffing two otherwise-identical panes.
Offer — your call on shape
Two things, and we would send them as separate PRs so you can take either independently:
for_agent_commandunchanged as the fallback when it is absent. Additive, does not alter any existing behaviour or test.doctor, or in presence is a design decision we would rather you made; a log line is the smallest version.Both would come with regression tests. We may well be duplicating something you already have unpushed — happy for these to be closed in favour of your version, and we would rather you reshaped them than merged ours as-is if the shape is wrong.