Venom
RFC-0018venom-spec

venom orchestrator UX model

Field Value
Status Accepted
Accepted 2026-11-08, explicitly approved by the maintainer
Authors commixon (commixon), with Claude
Created 2026-11-07
Linear COM-312; sub-agents COM-347; own and host modes COM-299, COM-353; escalation COM-342 (map)
Amended 2026-11-08 (venom-org/venom#80): section 8.3: host mode sets pane.attention on the take-over pane; 2026-11-08 (venom-org/venom-spec#70): issue links use GitHub issue numbers; agents.linear.* and the Linear sync are removed; 2026-11-08 (venom-org/venom-spec#76): section 10 names the context panel’s verbs, as venom-org/venom#50 built them. 2026-11-08 (venom-org/venom-spec#83, normative; explicitly accepted by the maintainer on 2026-11-09): section 13 adds Normal, Input and Terminal modes and a configurable leader tree, and sections 3, 8.1 and 10 move their keys to match
Supersedes None; amends the venom and venom-desk rows of docs/ecosystem.md (terminology only)
Superseded by None

Summary

This RFC fixes the user-facing model of venom, the orchestrator of coding agents: the words it uses (workspace, repo, agent, harness), the states an agent can be in and how they are shown, how agents that need a human reach that human, how tool calls and sub-agents are displayed, how an agent is scoped to a folder that holds many repos, how venom opens splits and hands an agent to its harness’s own TUI, and what the usage and settings screens contain. venom implements it. venomx mechanisms it relies on are already defined in RFC-0016; this RFC only says how venom uses them. The harness adapter contract it needs is listed in section 9 and gets its own RFC.

Motivation

venom is the P2 product (docs/ecosystem.md, section 5). Before it is broken into issues, the decisions below need one place to live, so that the TUI, the verbs, the event stream and the adapters agree on names and states. Today the ecosystem doc talks about “threads, sessions and turns” and venom-desk talks about “workspaces”, and venomx already has sessions (RFC-0007). Without a decision, three products would use the same words for different things.

The design was explored on a design canvas (nine TUI screens, Venom brand, trve theme): main agent view, needs-you inbox, tool calls, sub-agents, workspaces and scope, take-over split, split menu, usage, settings. The screens are the reference for the layouts described here; this RFC is the reference for behaviour.

Reference products: t3code (provider adapters, worktree per thread, context meter), herdr, and the Claude desktop app (session list, inline approvals, tool-call rows).

Design

1. Vocabulary

Term Meaning Not called
workspace Any folder the user adds to venom (~/venom-org, ~/scratch). It holds 0..n repos. project
repo A git root venom finds inside a workspace. Never configured by hand. project
worktree A git worktree venom creates for one agent in one repo. branch, sandbox
agent One run of a harness on a task, scoped to a workspace, some of its repos, or one repo. It has turns, tool calls, a state and a transcript. session, thread
sub-agent An agent started by another agent. Spawned sub-agents are started by venom; native sub-agents are the harness’s own (for example Claude Code’s Task tool). child session
harness The program venom drives: claude-code, codex, opencode, any ACP agent. agent, provider
turn One prompt and the work the agent does until it stops. step

Rules:

  • Products use these words in UI, docs, verbs and events. venom MUST NOT call an agent a “session”: venomx sessions (RFC-0007) are a different thing, and both are on screen together.
  • The harness’s own session id is kept as an attribute of the agent (harness_session) for resume and log matching. It is never the name of the thing.
  • A single repo opened in venom is a workspace with one repo. A folder with no git is a workspace with no repos; agents in it run in place.
  • venom-desk uses desktop or layout for its own concept, not “workspace”. The ecosystem doc is amended in the same PR as this RFC.

2. Agent states

Every agent is in exactly one state. Colours and glyphs come from the brand book (docs/brand/README.md, section 02); a state is never shown by colour alone.

State Glyph Token Meaning
queued ○ muted Waiting for a concurrency slot
running ◆ status-info A turn is in progress
needs_you ▲ status-warn The agent is blocked on a human: see section 3
failed ✕ status-danger The harness crashed, gave up, or hit a usage limit
settled ● status-ok The turn finished; waiting for the next prompt or a review
handed_over ⇄ muted The harness’s own TUI has the agent (section 8.3)
archived ○ muted Hidden from the list; transcript kept
queued ──► running ──► settled ──► archived
              │  ▲         │
              ▼  │         └──► running (new prompt)
          needs_you
              │
running ──► failed ──► running (retry) | archived
settled ──► handed_over ──► settled (harness TUI quit, take back)

stop:     queued ──► settled            (no turn ran)
          running | needs_you ──► settled   (turn interrupted)
restart:  needs_you ──► failed          (failed.daemon_restart, RFC-0019 §9.2)
  • A settled agent is unread until the user opens it. Unread rows are bold.
  • Stop (agent.stop) ends work, never hides the agent:
    • running or needs_you: venom calls the adapter’s interrupt (RFC-0019 section 4). Pending requests of the turn are cancelled: their inbox items are removed with agent.attention_cleared {by: "stop"}, and the harness gets cancel_turn where it is still waiting. The turn ends turn.ended {reason: interrupted} and the agent is settled. A harness without interrupt is stopped with the adapter’s stop instead, and the next send reattaches.
    • queued: the agent leaves the queue without starting a turn; its prompt stays in the input box, unsent. It is settled and not unread.
    • settled, failed, archived: no-op, success (idempotent).
    • handed_over: rejected with conflict.agent_handed_over; the harness TUI owns the turn. Take back (section 8.3) first.
  • Archive (agent.archive) of a queued, running or needs_you agent is a stop followed by settled → archived; both transitions are logged, so venom events sees two agent.state_changed events. Archiving a handed_over agent is rejected like stop. Edges into archived therefore only come from settled and failed.
  • handed_over across a daemon restart. The take-over split’s harness TUI is a venomx pane (host mode) or a child of a venom TUI client (own mode, recorded as tui:<pid>), never a child of the venom daemon, so it can outlive a restart either way. The binding in TUI mode records which. On start, for each handed_over agent venom checks it:
    • Own mode: the client still runs, so the agent stays handed_over and the client takes it back when its split ends.
    • The pane still runs (venomx reachable, and pane.list (RFC-0007) shows it with exited: false): the agent stays handed_over and venom watches the pane for pane.exited again.
    • Venomx can’t say, because it is unreachable: the agent stays handed_over, since a harness that may still run must not get a second writer (RFC-0019 section 7.4). agent.takeback --force overrides this once the pane has ended.
    • Otherwise (the client, or the pane, is gone or exited): venom runs the take-back of section 8.3 step 4 at once (catch_up, reattach), and the agent is settled, unread if any turn was imported.
  • Creation is public: agent.spawn, agent.import (RFC-0019 section 9.4, not built yet) and spawned sub-agents emit agent.created once, with the agent’s initial state, before any agent.state_changed for it. from in agent.state_changed is never null.
  • Only the first prompt waits for a slot. A send to a settled or failed agent is an explicit prompt and is not held back, so it can take the count over agents.max_concurrent.
  • settled agents are archived automatically after agents.archive_after (default "3d"). Archiving keeps the transcript and the worktree. agent.clean removes the worktrees and branches of archived agents, all of them or with --merged only those whose PR merged, and keeps and reports any with uncommitted or unpushed work. Running it automatically is COM-428.

3. Attention

An agent that needs a human has one attention kind. Kinds are ordered by urgency, and that order sorts the inbox and the “Needs you” group.

Kind Agent state Example Blocking RFC-0016 level
approval needs_you Bash wants zig build test; grant exec:zig yes urgent
question needs_you “Keep varint framing?” with options 1/2/3 yes urgent
plan needs_you A plan waits for approval yes urgent
failed failed zig build exited 1 after 3 retries yes warn
limit failed Usage limit hit; resumes at 15:00 yes warn
review settled Finished, unread diff no info

Where attention shows, all at once:

  1. The agent’s row, in the “Needs you” group (section 4).
  2. The inbox (venom inbox, key space i): every item above, blocking ones first, oldest first within a kind. Each item shows the last turns of context and its actions. Answering moves to the next item.
  3. venom’s own status bar: ▲ 3 need you fills gold while anything blocks. venom draws this bar on the bottom row of the area it draws in both chrome modes: the terminal in ui.chrome = .own, its control pane in ui.chrome = .host (RFC-0016 section 10.1, with layout examples). venomx’s status line stays at the top of the terminal and carries no venom segment; venom publishes no venom.* segments in v1 (RFC-0016 section 10.2). Outside venom’s pane, items 4 and 5 reach the user.
  4. Any pane showing that agent: venom sets pane.attention with the level from the table and the attention reason, and clears it when the item is answered anywhere (RFC-0016 section 6).
  5. Outside the terminal: a notify.post (RFC-0016 section 6) after attention.notify_after (default "2m") unanswered, for blocking kinds only; a BEL for blocking kinds if attention.bell = true. notify.post needs write on venomx’s server:root, which no pane may hold, so venom serve posts it, outside any pane: it reads venomx’s owner file only to issue itself a short-lived token, kept in memory and never passed to a harness or pane (RFC-0005 section 3.7). Started inside a pane, venom serve is refused, logs it once, and posts nothing. In host mode a BEL currently stays info in venomx and makes no sound.
  6. Scripts: venom events --json emits agent.attention (section 11).

Actions:

Kind Keys
approval y allow once, a always (narrowest matching grant), e edit command, n deny, r deny with reason
question 1–9 pick an option, or type an answer
plan y approve, n reject with feedback
failed, limit R retry, o open agent
review o open, x archive
any s snooze 15m, \ open in split, m mark

Batch actions (Y / N on marked items) are allowed only for approvals that request the same grant.

An unanswered approval waits forever by default. attention.approval_timeout MAY set a timeout, after which the request is denied with the reason timeout.

4. Agent list

The left column of the main view lists agents across all workspaces.

  • Grouping (key g cycles): state (default), workspace, repo.

    • state: groups Needs you (needs_you and failed), Running (queued, running, handed_over), Settled. Archived agents are one folded line at the bottom in every mode.
    • workspace: one group per workspace, rows sorted by state urgency.
    • repo: one group per repo; multi-repo agents in a group named after their workspace (~/venom-org (multi-repo)); agents in a repo-less workspace in a group named after it.
  • Row: glyph, title, age on line 1; harness, scope, sub-agent count and the current activity or attention reason on line 2:

    ▲ venom-echo conformance                    1m
      claude · venom-std · 2 sub · approve: zig build test
  • Children (key t cycles, config ui.children): tree shows sub-agents nested under their parent; folded shows one summary line (3 children · ●1 ◆1 ▲1); hidden removes them from the list.

  • Bubbling: a child’s needs_you or failed adds a count to every ancestor’s row (▲ 1). In hidden mode the count is the only trace of the children and appears only while one needs the human. venom’s status bar counts children too.

  • Filter (/) matches harness, repo, issue id and title.

5. Transcript and tool calls

The centre of the main view shows the selected agent: prompts, agent text, tool calls, sub-agent blocks and inline attention.

One row per tool call:

▸ Edit      venom-std/src/proto/frame.zig                 +6 −4
▸ Bash      zig build test                         exit 0 · 4.2s
▾ Bash      zig build -Dtarget=wasm32-wasi         exit 1 · 2.0s
    src/proto/codec.zig:88:17: error: use of undeclared identifier 'Frame'

Density (key v cycles, config ui.transcript.density):

Density Shows
compact One row per call; bodies closed
normal (default) As compact, plus edit diffs open with the first 6 lines
verbose Every body open, in full

Regardless of density, these are always open: a call waiting for approval, a failed call (non-zero exit or tool error), and a call running longer than ui.transcript.open_running_after (default "5s", with a live tail).

Grouping: consecutive reads, searches and listings merge into one Explored row (6 reads · 2 searches · 1 list); repeated edits to one file in one turn merge into Edit ×N; a sub-agent’s calls are one row that opens the sub-agent (]). enter toggles a row, E / C expand or collapse all, p opens a body in the pager, J shows the raw tool JSON.

The input box at the bottom shows the permission mode, the model, and the context meter (section 10). ⏎ steers or queues, as the harness allows; ⇧⏎ queues; ⌥⏎ interrupts and sends. ⇧⏎ needs a terminal with the Kitty keyboard protocol. A prompt that can’t be sent returns to that agent’s own draft. While the agent is handed_over the box is disabled.

Pasted text is never a command, on any screen: it goes into a text box or a shell split, and everywhere else it does nothing. A pasted line break does not send, submit or approve.

6. Agents that spawn agents

Spawned Native
Started by venom (venom agent spawn, called by the parent through venom’s MCP server) The harness itself (e.g. Claude Code Task)
Worktree Its own The parent’s
Promptable by the human Yes; the parent sees the message No: “Message the parent to steer it”
Stoppable Yes Where the harness allows it
Promote to top level (P) Yes, by a person; agents are denied No
Glyph in the tree ⑂ ·
  • A parent that fans out shows a fan-out block in its transcript: one small card per child with its state, last activity and result, plus actions open child (]), split view (\), wait for all (agent.wait), stop all (agent.stop --tree).
  • [ goes to the parent, ] into the focused child; the header shows a breadcrumb (rename proto.Frame › venomx).
  • A child inherits the parent’s grants, model and permission mode, never more. agents.spawn.depth (default 2) limits the tree depth, counting the top-level agent as 1, so the default allows children and denies grandchildren (denied.spawn_depth). agents.max_concurrent (default 6) counts children.
  • Mode width is accept_edits > ask > plan. A child may only narrow its parent’s mode (denied.spawn_mode) and may not name a model other than its parent’s (denied.spawn_model), a scope outside its parent’s (denied.spawn_scope), or grants beyond its parent’s (denied.delegation). A child of an isolated parent can’t run in place (denied.spawn_isolation). A harness without that mode gets plan’s restrictions or a refusal, never more. A child’s grants survive a restart exactly, or it does not start.
  • Wait and stop. agent.wait holds its call until every agent below this one has finished (settled, failed or archived), or until timeout_s (default 600) runs out and it reports done: false. agent.stop --tree stops the agent and every agent below it, parents first; one in handed_over stays as it is and shows in the result’s children.
  • A token an agent issued traces back to that agent for every limit here.
  • Promote is for people. agent.promote called by an agent fails with denied.promote, since promoting itself or a child would escape the depth limit.
  • Cross-agent writes: when an agent asks to write in a repo that another live agent in the same tree owns, the approval offers route to that agent as its primary action besides allow and deny. Routing sends the requested change as a message to the owning agent.

7. Workspaces and scope

  • A workspace is a folder path. Its repos are the git roots under it, found on add and on rescan (R), not configured.

  • Workspaces MAY nest; an agent belongs to the workspace closest to its scope root.

  • Scope of a new agent: the whole workspace, a set of its repos, or one repo. The agent starts at the workspace root. Writes to repos outside its scope ask first and are never covered by “allow always”. This is best-effort routing, not containment: what each harness can and cannot catch is listed in RFC-0019 section 12.

  • Isolation: worktree (default: one worktree per repo in scope), in_place, or copy. Worktrees are siblings of the repo: <repo>-<n>/ when the agent is linked to issue <n> with --issue (the hub convention), else <repo>-<agent-id>/, on the branch given with --branch or venom/<agent-id>. --issue takes <n>, #<n> or owner/repo#<n>, and only the number is used in the folder name. The link names folders and changes nothing on the tracker.

  • The new-agent dialog previews what it will create before it starts:

    Will create
    + ~/venom-org/venom-spec-246/   branch 246-rfc-0002-hello-caps
    + ~/venom-org/venom-std-246/    branch 246-rfc-0002-hello-caps
  • Settings scope: three files, the narrowest winning per key:

    Layer File
    user $XDG_CONFIG_HOME/venom/config.zon ($HOME/.config/venom/config.zon if XDG_CONFIG_HOME is unset or empty): venom-std cfg’s standard user file (RFC-0001)
    workspace <workspace>/.venom/workspace.zon
    repo <repo>/.venom/repo.zon
    • The workspace and repo files are cfg overlay layers above the user file. A missing file is an empty layer.

    • With nested workspaces, only the closest workspace’s file applies to a repo or agent; outer workspaces’ files are not stacked.

    • Keys per layer. Workspace and repo files may set only what work in that folder should change:

      May be set in workspace and repo files User-only
      agents.default, agents.mode, agents.isolation, agents.archive_after, agents.spawn.*, attention.* grants, log, ui.*, agents.max_concurrent, and any key not listed on the left

      grants is user-only so that a cloned repo can never widen what callers may do; ui.* because there is one TUI per user; agents.max_concurrent because it is a daemon-wide limit. A workspace or repo file that sets a user-only key is rejected with invalid_params.config_scope, naming the key and the file; so is config set of a user-only key with a scope flag. New keys are user-only unless this list is amended.

    • config get and config set take optional --workspace <path> and --repo <path> (absolute, or relative to --workspace). get reads the value in effect there and reports the layer and file each value comes from; set writes the narrowest named file (the repo file with --repo, else the workspace file). Without either flag both work on the user file. set changes only that key, keeping comments and formatting, writes through a symlink and keeps the file’s mode. If it can’t prove the edit correct, or the file is read-only, it fails (failed.config_edit) and leaves the file untouched.

      $ VENOM_TOKEN=@owner venom config set --key=agents.isolation --value=.in_place --workspace ~/scratch
      $ VENOM_TOKEN=@owner venom config set --key=ui.chrome --value=.own --workspace ~/scratch --json
      {"error":{"code":"invalid_params.config_scope","message":"this key is user-only; set it without --workspace or --repo","data":{"param":"key","key":"ui.chrome","source":"workspace","path":"/home/me/scratch/.venom/workspace.zon"}}}

8. Splits, background and take-over

8.1 User verbs

The user never sees venomx terms. venom offers:

Action Key Effect
Start ⏎ Start the agent and show it in the main view
Run in background ⇧⏎ Start the agent with no view; it appears in the list
Start in split / Open in split ⌃⏎ / \ Open the split menu (8.2) for the agent
Close split space v x Close the view; the agent keeps running

Agents live in the venom daemon, never in a pane. Closing a split, the venom TUI or a terminal never stops an agent.

8.2 Split menu

Option Opens Availability
Agent view (default) venom’s view of that agent: transcript, tool calls, approvals, input box Always
Shell in worktree $SHELL in the agent’s worktree Agents with a worktree or in-place scope
Take over in <harness> The harness’s own TUI on the same conversation (8.3) Harnesses whose adapter provides resume (section 9)

Mechanics:

  • With venomx reachable (ui.chrome = .host or .auto, RFC-0016 section 10), a split is a venomx pane created by pane.split with near tags (RFC-0016 section 9) and the agent’s cwd and environment (RFC-0013). An Agent view split runs venom agent view <agent>; panes never receive draw commands (RFC-0016, Roles).
  • No pane holds daemon-wide write. venom’s control pane has write only on itself and the panes it opens, and a process in a split has read: it can’t send to, kill or split any pane, here or in another session. A child keeps its pane’s rights by ancestry (RFC-0005), whatever its environment says. VENOM_TOKEN and VENOMX_TOKEN are not passed to a shell or take-over child; an Agent view keeps the TUI’s narrow VENOM_TOKEN.
  • Without venomx (ui.chrome = .own), venom draws the same options as its own side-by-side layout. The user’s action and keys do not change.

8.3 Take-over

Only one client can drive a harness process at a time, so take-over is an explicit handover with its own state:

  1. Allowed only between turns. Mid-turn, venom asks: after this turn (default), interrupt now, cancel.
  2. venom releases its protocol connection and opens a split running the harness’s resume command in the agent’s cwd, for example claude --resume <harness_session>.
  3. The agent is handed_over (⇄ in claude-code). venom’s view of it shows a banner with Focus split and Take back, and the input box is disabled. Approvals happen in the harness TUI. If the adapter reports attention (section 9, item 5), the agent still moves to “Needs you”; own mode marks the split, and host mode sets pane.attention on its venomx pane (RFC-0019 section 7.3). Otherwise it stays ⇄.
  4. When the harness TUI exits (pane exit, RFC-0012 / RFC-0017), or on Take back (T) where this client can close the split, venom resumes the same conversation in protocol mode, imports the turns that ran meanwhile, including usage, and marks the agent settled. While the harness TUI may still run there is one writer only: take-back is refused and no turn starts (RFC-0019 section 7.4).

9. Harness adapter requirements

These are the requirements this RFC puts on adapters. The adapter contract itself is RFC-0019 (open question 1).

  1. resume: a command that reopens a conversation in the harness’s TUI, given harness_session and cwd. Without it, the take-over option is not offered.
  2. idle boundary: a signal that a turn has ended.
  3. release / reattach: stop driving the process cleanly, and later resume the same conversation in protocol mode.
  4. catch-up: a readable transcript (file or API) to import turns and usage that happened outside venom.
  5. attention while away (optional): a hook that reports “waiting for approval or input”.
  6. usage: per-turn token counts (input, output, cache read, cache write) and, for subscription plans, the plan’s window percentages when the harness exposes them.

10. Usage

  • Per agent: tokens in, out, cache read, cache write; turns; tool calls; wall time. A tree (parent and children) also shows its total.
  • Per harness: for subscription plans, the plan windows the harness reports (for example a 5-hour and a weekly window, as percentages, with reset times). For API keys, a cost estimate from list prices, labelled as an estimate, with an optional daily budget and alert.
  • venom MUST NOT invent a cost for subscription plans; it shows quota only.
  • Context meter in the input box: 62% · 124k / 200k, as ten cells; it turns gold above ui.context.warn_at (default 80%). space a c opens the context panel (agent.context): what fills the window (system and instructions, tool definitions, messages and tool results, files read), estimated and labelled so (RFC-0019 section 8.2), the largest items, and the actions below. An action shows only when the agent’s adapter declares it.
    • Compact now (agent.compact): the harness compacts, as a turn.
    • Fork to a fresh agent with a summary (agent.fork): a new sub-agent in the source’s folder, on the harness’s copy of the conversation. venom makes no model calls itself, so the summary is the fork’s first prompt asking for one. The verb returns at once, and the panel moves to the new agent when agent.fork_ended arrives.
    • Turn off MCP servers this agent hasn’t used (agent.mcp_off): shown when there is one. Its next harness process starts without them. venom’s own servers are never listed.
  • The usage screen groups by workspace, agent or harness over today, 7 days or 30 days, and exports CSV.

11. Settings

Every field on the settings screen is a config key, edited through config set (RFC-0001). The screen shows the pending ZON diff and the equivalent command. Sections: General, Workspaces, Agents, Models and limits, Permissions and grants, Worktrees, Attention, Transcript, Usage and budgets, Appearance, Keys, Integrations, Raw config.

.{
    .agents = .{
        .default = .claude_code,
        .mode = .ask,
        .isolation = .worktree,
        .max_concurrent = 6,
        .spawn = .{ .allow = true, .depth = 2 },
        .archive_after = "3d",
    },
    .attention = .{
        .bell = true,
        .notify_after = "2m",
        .approval_timeout = null,
    },
    .ui = .{
        .chrome = .auto,
        .group_by = .state,
        .children = .tree,
        .transcript = .{ .density = .normal, .open_running_after = "5s" },
        .context = .{ .warn_at = 80 },
        .keys = .{ .leader = "space", .leave_terminal = "ctrl+]", .popup_delay_ms = 200, .timeout_ms = 0 },
    },
}

venom finds harnesses on PATH, shows their version and sign-in state, and never handles vendor credentials: a harness that is not signed in shows how to sign in with its own command.

12. What lives in venomx and what lives in venom

Rule: a mechanism that more than one product needs (venom, venom-os, venom-desk) is implemented in venomx; everything else is implemented in the product that needs it. A mechanism moves into venomx when its second consumer appears, not before.

Need Lives in Defined by
Opening a pane with a cwd and environment venomx RFC-0007, RFC-0013, RFC-0016 §9
Pane exit signal venomx RFC-0012, RFC-0017
Pane attention, notifications venomx RFC-0016 §6
Terminal status line (top by default) and the status segment mechanism venomx RFC-0016 §5; venom publishes no segments in v1 (§10.2)
venom’s own status bar (bottom of venom’s area, own and host modes) venom This RFC §3, RFC-0016 §10.1
Floating panes and popups venomx RFC-0016 §8
Agents, states, inbox, approvals, grants decisions venom This RFC
Transcript and tool-call rendering venom This RFC
Sub-agent tree and its display modes venom This RFC
Workspaces, repo discovery, worktrees, issue link venom This RFC (moves out if venom-desk needs it)
Harness adapters and take-over venom This RFC, adapter RFC
Usage and quota accounting venom This RFC
Own-mode split layout venom (venom-term widgets) This RFC
Key router and leader popup widget venom-term, drawn by each client RFC-0016 §7.6

13. Keys and modes

This section applies RFC-0016 section 7.5’s grammar to venom. It works the same with or without venomx, except where 13.5 says otherwise.

13.1 Modes

A mode decides where keys go. A view (list, transcript, inbox, settings) only decides what the bare keys do.

Mode Where Keys Enter Leave
Normal Every view Bare keys and the leader tree esc from Input Not applicable
Input The agent’s input box, the filter, text fields, the new-agent dialog Text and the send keys i, /, or focusing a field esc
Terminal A shell or take-over split in own mode Everything goes to the program Opening or focusing the split ui.keys.leave_terminal (ctrl+])

The status bar MUST show NORMAL, INPUT or TERMINAL, and the cursor SHOULD change shape in Input.

  • esc goes back one step: popup, then Input, then Normal, then the previous view. It keeps the draft, and it never interrupts an agent.
  • Background events MUST NOT change the mode. If an approval arrives while the user types, y stays text.
  • Input takes only text and its own keys: ⏎, ⇧⏎ and ⌥⏎ send, esc leaves, and ⌃c clears the draft. Editing keys such as ⌃w, ⌃u and ⌃a edit. Under venomx, ⌃a goes to the presentation client first (RFC-0016 section 7.2).
  • After sending, the box stays in Input. ⏎ on a list row opens the agent in Normal, where the approval keys work.
  • The new-agent dialog is a form, so space picks a repo there and the leader is inactive.
  • ⌃c in Normal and q quit the TUI; agents keep running.

13.2 Leader

Key Default Meaning
ui.keys.leader space venom’s leader, active only in Normal
ui.keys.leave_terminal ctrl+] Leaves Terminal mode
ui.keys.popup_delay_ms 200 As RFC-0016 section 7.6
ui.keys.timeout_ms 0 As RFC-0016 section 7.6

A plain key is a safe leader here because Input and Terminal never see it. doctor MUST warn when ui.keys.leader equals venomx’s keys.leader in host mode, because venomx would take that key first and venom’s tree could never open from it. venom draws the popup with venom-term’s widget.

13.3 Bare keys in Normal

Bare keys act on what is under the cursor. On top of the keys of sections 3 to 6:

Key Action
j k, ↑ ↓, ⏎, / Move, open, filter
i Enter Input on the selected agent
d Diff of the selected agent
n New agent here
\ Split menu
! Next item that needs you
? Keys for this view
h l, ← → Fold groups in the list; change the value in settings

13.4 Leader tree

Keys Action
space space Fuzzy agent switcher
space a a / n / N Agents screen / new agent here / new agent, pick workspace
space a s / x / f / c Stop / archive / fork / context panel
space w w / a / r Workspaces / add a folder / rescan
space v v / s / t / x Split menu / shell / take over / close
space v h j k l, space v ← ↓ ↑ → Move between splits (own mode)
space i / s / d / u Inbox / settings / diff / usage
space : / ? Command prompt over venom’s verbs / all keys

13.5 Standalone and under venomx

ui.chrome = .own ui.chrome = .host
The space tree As 13.4 As 13.4, in the control pane and in agent views
space v s (shell split) venom’s own split, in Terminal mode A venomx pane, so no Terminal mode
Moving between splits space v then a direction ctrl+b then a direction
From another pane Not applicable ctrl+b v, then the same tree
  • No focus verb. space v then a direction exists only in own mode: no verb moves a client’s focus (RFC-0016 section 3), so in host mode those rows are absent from the popup.
  • ctrl+b v X equals space X. venom registers table venom under prefix v, with no when, generated from the same table as its own tree. Only bindings with a verb or exec target can run from outside venom. In v1 those are i, s, d and u, and others join as they gain such a target.
  • A binding that needs an agent uses the focused pane’s agent. From a pane with no agent it fails with a notification (RFC-0016 section 7.3).
  • keys.prefixes moves v (RFC-0016 section 7.4). Standalone venom needs none of this.

Agent-contract impact

  • Verbs (venom schema, RFC-0004), all with --json:

    • workspace.add|list|remove|scan
    • agent.spawn|list|show|send|stop|archive|promote|wait; agent.stop takes --tree
    • agent.tool (one tool call whole), agent.clean
    • agent.view, agent.split, agent.diff (product-local commands, RFC-0011), agent.takeover, agent.takeback; agent.takeover_split, agent.takeover_pane, agent.hook and harness.hook are for clients and hooks (RFC-0019 section 7)
    • harness.list, harness.show; always.list, always.revoke (RFC-0019)
    • agent.context, agent.compact, agent.fork, agent.mcp_off (section 10, RFC-0019)
    • inbox.list, inbox.answer, inbox.batch, inbox.snooze
    • usage.show, usage.export
  • Example:

    $ venom agent spawn --workspace ~/venom-org --repos venom-spec,venom-std --isolation worktree --issue 246 --task "Add hello capability bits" --json
    {"agent":"a12","state":"queued","worktrees":["venom-spec-246","venom-std-246"]}
    $ venom inbox list --json
    {"items":[{"agent":"a2","kind":"question","age_s":248,"reason":"keep varint framing?"}]}
    $ venom agent wait --agent=a1 --json
    {"agent":"a1","done":true,"children":[{"agent":"a2","state":"settled"}]}
  • Events on venom events --json: agent.created {agent, title, harness, workspace, repos, parent, state} (repos is empty for a whole-workspace scope, parent is null for a top-level agent), agent.state_changed {agent, from, to}, agent.attention {agent, kind, reason, age_s}, agent.attention_cleared {agent, by}, agent.tool_call {agent, call, verb, status}, agent.usage {agent, tokens_in, tokens_out, cache_read, cache_write}, agent.promoted {agent, parent} (parent is who it belonged to), agent.child_prompted {agent, child, text} (agent is the parent; logged when anyone but the parent prompts its child).

    {"ev":"agent.attention","agent":"a2","kind":"question","reason":"keep varint framing?","age_s":248}
  • Grants (RFC-0005): reads are read. agent.spawn, agent.send, agent.stop need write on the workspace; agent.promote needs write on the agent and is denied to agents; inbox.answer needs write on the agent and is never granted to the agent being answered or its descendants, and an agent may deny, reject, cancel or answer a question but never allow or approve (denied.agent_approval); config set needs cfg:write.

  • skill, doctor: doctor checks each harness (found, version, signed in, protocol reachable) and each workspace (path exists, repos found).

  • Headless: every screen is a view over verbs and events; nothing in this RFC requires the TUI.

Alternatives

  • “Project” mapped to one repo, as most orchestrators do. Rejected: it can’t express a folder of related repos, and it collides with tracker projects, such as GitHub’s.
  • “Session” or “thread” for an agent. Rejected: collides with venomx sessions; “agent” matches the CLI verb and the brand voice.
  • Splits always show the harness’s own TUI. Rejected as the default: most harnesses can’t be driven by venom and shown natively at once, and the inbox and approvals would stop working. Kept as explicit take-over.
  • venom draws agents itself and never uses venomx. Rejected: loses shared attention, notifications and placement in venom-os. Kept as the own fallback.
  • venom’s counts as venom.* segments in venomx’s line (no venom bar in host mode). Rejected for v1 by the maintainer (2026-11-07, COM-360): one bar per owner is clearer, and venom’s bar looks the same in both modes. RFC-0016 section 10.2 keeps the door open.
  • Stop of a queued agent goes straight to archived. Rejected (COM-372): stop must not hide an agent; archive already covers that, and is defined as stop then archive.
  • Always end handed_over on daemon start. Rejected (COM-372): in host mode the harness TUI is a venomx pane that survives the venom daemon, and reattaching while it still drives the conversation would put two clients on it (RFC-0019 section 9.4).
  • Announce creation as agent.state_changed {from: null}. Rejected (COM-372): consumers would need a null case on every transition, and the event would not carry the title, harness and scope a list needs.
  • Flat single keys with no leader (this RFC’s first key set). Letters ran out, nothing was discoverable outside ?, and keys meant different things per view.
  • ⌃c, ⌃u and ⌃w working from the input box. These break habits from every other text input, and ⌃c quit the TUI in the middle of a prompt.
  • A second leader that works inside text fields. esc space already does this with one more key, and it needs no extra setting.
  • Do nothing. Each screen would invent its own words and states.

Open questions

  1. Harness adapter RFC. Answered by RFC-0019. Section 9 lists requirements; the adapter contract (capabilities, send, steer, interrupt, approvals, fork) needed its own RFC before adapters are built.
  2. Persistence across daemon restarts. Moved (COM-317). Transcripts and pending approvals survive a daemon restart through venom’s durable event log. Harness processes, reattaching to them and conversations venom did not start are specified in RFC-0019 section 9 (Daemon restart and adoption).
  3. One task on several harnesses. Should venom run the same task on Claude Code and Codex and compare the results? It would add a “task with attempts” level above agents. Deferred to COM-356.
  4. Native sub-agent steering. Beyond view and stop, what can venom do with native sub-agents, per harness? Deferred to COM-357.
  5. Remote and headless agents. Agents on another machine or in a cloud sandbox, with venom as the client. Not needed for P2, but it affects the wire protocol. Deferred to COM-358.
  6. Workspaces in venom-desk. If venom-desk needs to open a workspace, the concept moves to a shared place (section 12). Deferred to COM-359.
  7. Escalation defaults. Resolved (COM-317). The defaults stay: attention.notify_after = "2m", the bell for blocking kinds only, and no approval timeout by default (attention.approval_timeout = null), as in sections 3 and 11.

Questions 3–6 are tracked as open-question issues in this repo; the map gives each COM-n its GitHub number.