Executors¶
Executors are subprocess adapters in internal/executor.
Interface¶
type Interface interface {
CheckAuth(ctx context.Context) AuthStatus
Execute(ctx context.Context, req Request) Result
BuildPrompt(req PromptRequest) string
ExtractCommand(commentContent string, mentionKeyword string) string
}
Supported Executors¶
| Executor | Command |
|---|---|
| Claude | claude -p - --output-format=stream-json --verbose --dangerously-skip-permissions |
| Codex | codex exec --dangerously-bypass-approvals-and-sandbox --json |
| Goose | goose run -t - --output-format stream-json --no-session |
| Pi | pi --mode json -p - --no-session -a |
The selected executor comes from --executor, KARDBRD_AGENT_EXECUTOR, or the executor field in kardbrd.yml.
Executor subprocesses receive KARDBRD_TOKEN and KARDBRD_API_URL so prompts can call kardbrd ... commands.
Request.Model and Request.ReasoningEffort are per execution values. Codex receives --model and --config model_reasoning_effort="..."; Claude receives --model and --effort. The manager passes the same values to its bounded publication continuation. A missing value uses the executor's own default. Goose and Pi reject reasoning effort because their adapters have no verified control for it; their resume calls retain model selection.
Progress and terminal summaries¶
Executors may add comments, attachments, links, reactions, and other card updates while they work. Those updates are progress only: they do not end a run and are never used as proof of completion.
Previously the manager inferred completion from a recent bot comment, which could mistake progress for a final summary. Now it uses the executor's returned final assistant text.
For a normal successful run, the executor returns its final assistant text and the agent manager publishes that text once as the terminal card summary, then adds the success reaction. If a same-card mention arrives while a session is active, the manager acknowledges it with 👀, coalesces it with any pending follow-up, and runs it after the active session clears. The success reaction is never added until the terminal-comment request succeeds. If that request fails, the manager reports the failure without retrying the non-idempotent summary post or marking the run successful. Only an empty final response can use the bounded session-resume recovery path; empty or failed recovery remains visibly non-successful.
Codex JSONL compatibility¶
The Codex adapter supports the current codex exec --json event stream: it records
thread.started.thread_id, treats nested completed item.type=agent_message records as
the JSONL compatibility fallback, and fails on turn.failed, top-level error, malformed
JSONL, a modern stream missing turn.completed, or a nonzero process exit. The documented
phase-less completed-agent-message shape is accepted; explicit commentary-phase messages are
progress, not a fallback terminal summary. Older top-level item.message and
response.message payloads remain supported for legacy CLI fixtures.
For real Codex subprocesses, JSONL is not the terminal-summary source. Each invocation passes
the CLI a unique private --output-last-message path and uses that bounded final-only file for
ResultText after successful process completion. The adapter holds the original file descriptor
while Codex runs, so replacing the path cannot substitute a file for publication. The private
directory and file are removed on success, failure, timeout, or cancellation. A missing or
oversized output file is an adapter failure; a present empty file remains an empty terminal
result, so the manager can show its existing bounded recovery outcome rather than publishing a
false success.
On Unix, the shared subprocess runner owns a separate process group plus parent-owned stdout and stderr and stdin pipes. It terminates descendants that remain in that process group on every terminal path, bounds captured diagnostics, and drains output after the child exits, so a slow progress callback or an inherited input descriptor cannot discard a terminal JSONL line or make completion wait indefinitely. Progress delivery is bounded and best-effort under backlog; scanner/read errors remain executor failures.
Diagnostic retention has independent byte caps. For Codex, each complete stdout record is decoded into compact protocol state before that retained diagnostic cap, so verbose tool output cannot turn a later completion or failed-turn record into malformed JSONL. For Codex, hitting a retained-log cap annotates the diagnostic; it is not by itself an execution failure. Claude, Goose, and Pi retain their complete stdout protocol stream because they parse it after process exit; stderr remains bounded diagnostics and its truncation alone does not change a successful exit into a failure.
Progress buffering has both item and byte caps, and oversized assistant progress is skipped rather than retained. Known executor credentials are redacted from Codex progress and terminal text before they reach board-visible result handling.
Nested assistant messages continue to stream as progress, with repeated item snapshots
suppressed. Reasoning, command execution, and raw tool payloads are never forwarded as Codex
assistant chunks. If the manager needs empty-result recovery and the adapter supplied a thread
ID, Codex uses codex exec resume [options] -- <SESSION_ID> with the original CWD and prompt on
stdin; it never silently starts a new codex exec task. The manager still owns the only terminal
card publication and never resumes after receiving a non-empty final result.