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
settledagent is unread until the user opens it. Unread rows are bold. - Stop (
agent.stop) ends work, never hides the agent:runningorneeds_you: venom calls the adapter’sinterrupt(RFC-0019 section 4). Pending requests of the turn are cancelled: their inbox items are removed withagent.attention_cleared {by: "stop"}, and the harness getscancel_turnwhere it is still waiting. The turn endsturn.ended {reason: interrupted}and the agent issettled. A harness withoutinterruptis stopped with the adapter’sstopinstead, and the nextsendreattaches.queued: the agent leaves the queue without starting a turn; its prompt stays in the input box, unsent. It issettledand not unread.settled,failed,archived: no-op, success (idempotent).handed_over: rejected withconflict.agent_handed_over; the harness TUI owns the turn. Take back (section 8.3) first.
- Archive (
agent.archive) of aqueued,runningorneeds_youagent is a stop followed bysettled → archived; both transitions are logged, sovenom eventssees twoagent.state_changedevents. Archiving ahanded_overagent is rejected like stop. Edges intoarchivedtherefore only come fromsettledandfailed. handed_overacross 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 astui:<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 eachhanded_overagent venom checks it:- Own mode: the client still runs, so the agent stays
handed_overand the client takes it back when its split ends. - The pane still runs (venomx reachable, and
pane.list(RFC-0007) shows it withexited: false): the agent stayshanded_overand venom watches the pane forpane.exitedagain. - 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 --forceoverrides 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 issettled, unread if any turn was imported.
- Own mode: the client still runs, so the agent stays
- Creation is public:
agent.spawn,agent.import(RFC-0019 section 9.4, not built yet) and spawned sub-agents emitagent.createdonce, with the agent’s initial state, before anyagent.state_changedfor it.frominagent.state_changedis never null. - Only the first prompt waits for a slot. A send to a
settledorfailedagent is an explicit prompt and is not held back, so it can take the count overagents.max_concurrent. settledagents are archived automatically afteragents.archive_after(default"3d"). Archiving keeps the transcript and the worktree.agent.cleanremoves the worktrees and branches of archived agents, all of them or with--mergedonly 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:
- The agent’s row, in the “Needs you” group (section 4).
- The inbox (
venom inbox, keyspace 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. - venom’s own status bar:
▲ 3 need youfillsgoldwhile anything blocks. venom draws this bar on the bottom row of the area it draws in both chrome modes: the terminal inui.chrome = .own, its control pane inui.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 novenom.*segments in v1 (RFC-0016 section 10.2). Outside venom’s pane, items 4 and 5 reach the user. - Any pane showing that agent: venom sets
pane.attentionwith the level from the table and the attention reason, and clears it when the item is answered anywhere (RFC-0016 section 6). - Outside the terminal: a
notify.post(RFC-0016 section 6) afterattention.notify_after(default"2m") unanswered, for blocking kinds only; a BEL for blocking kinds ifattention.bell = true.notify.postneedswriteon venomx’sserver:root, which no pane may hold, sovenom serveposts 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 serveis refused, logs it once, and posts nothing. In host mode a BEL currently staysinfoin venomx and makes no sound. - Scripts:
venom events --jsonemitsagent.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
gcycles):state(default),workspace,repo.state: groups Needs you (needs_youandfailed), 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
tcycles, configui.children):treeshows sub-agents nested under their parent;foldedshows one summary line (3 children · ●1 ◆1 ▲1);hiddenremoves them from the list. -
Bubbling: a child’s
needs_youorfailedadds a count to every ancestor’s row (▲ 1). Inhiddenmode 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(default2) 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(default6) 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 getsplan’s restrictions or a refusal, never more. A child’s grants survive a restart exactly, or it does not start. - Wait and stop.
agent.waitholds its call until every agent below this one has finished (settled,failedorarchived), or untiltimeout_s(default600) runs out and it reportsdone: false.agent.stop --treestops the agent and every agent below it, parents first; one inhanded_overstays as it is and shows in the result’schildren. - A token an agent issued traces back to that agent for every limit here.
- Promote is for people.
agent.promotecalled by an agent fails withdenied.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, orcopy. 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--branchorvenom/<agent-id>.--issuetakes<n>,#<n>orowner/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.zonifXDG_CONFIG_HOMEis unset or empty): venom-std cfg’s standard user file (RFC-0001)workspace <workspace>/.venom/workspace.zonrepo <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 leftgrantsis user-only so that a cloned repo can never widen what callers may do;ui.*because there is one TUI per user;agents.max_concurrentbecause it is a daemon-wide limit. A workspace or repo file that sets a user-only key is rejected withinvalid_params.config_scope, naming the key and the file; so isconfig setof a user-only key with a scope flag. New keys are user-only unless this list is amended. -
config getandconfig settake optional--workspace <path>and--repo <path>(absolute, or relative to--workspace).getreads the value in effect there and reports the layer and file each value comes from;setwrites the narrowest named file (the repo file with--repo, else the workspace file). Without either flag both work on the user file.setchanges 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 = .hostor.auto, RFC-0016 section 10), a split is a venomx pane created bypane.splitwithneartags (RFC-0016 section 9) and the agent’s cwd and environment (RFC-0013). An Agent view split runsvenom agent view <agent>; panes never receive draw commands (RFC-0016, Roles). - No pane holds daemon-wide write. venom’s control pane has
writeonly on itself and the panes it opens, and a process in a split hasread: 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_TOKENandVENOMX_TOKENare not passed to a shell or take-over child; an Agent view keeps the TUI’s narrowVENOM_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:
- Allowed only between turns. Mid-turn, venom asks: after this turn (default), interrupt now, cancel.
- 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>. - 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 setspane.attentionon its venomx pane (RFC-0019 section 7.3). Otherwise it stays⇄. - 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 agentsettled. 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).
- resume: a command that reopens a conversation in the harness’s TUI,
given
harness_sessionand cwd. Without it, the take-over option is not offered. - idle boundary: a signal that a turn has ended.
- release / reattach: stop driving the process cleanly, and later resume the same conversation in protocol mode.
- catch-up: a readable transcript (file or API) to import turns and usage that happened outside venom.
- attention while away (optional): a hook that reports “waiting for approval or input”.
- 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 turnsgoldaboveui.context.warn_at(default 80%).space a copens 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 whenagent.fork_endedarrives. - 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.
- Compact now (
- 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.
escgoes 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,
ystays text. - Input takes only text and its own keys:
⏎,⇧⏎and⌥⏎send,escleaves, and⌃cclears the draft. Editing keys such as⌃w,⌃uand⌃aedit. Under venomx,⌃agoes 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
spacepicks a repo there and the leader is inactive. ⌃cin Normal andqquit 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 vthen 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 Xequalsspace X. venom registers tablevenomunder prefixv, with nowhen, 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 arei,s,dandu, 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.prefixesmovesv(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|scanagent.spawn|list|show|send|stop|archive|promote|wait;agent.stoptakes--treeagent.tool(one tool call whole),agent.cleanagent.view,agent.split,agent.diff(product-local commands, RFC-0011),agent.takeover,agent.takeback;agent.takeover_split,agent.takeover_pane,agent.hookandharness.hookare 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.snoozeusage.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}(reposis empty for a whole-workspace scope,parentis 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}(parentis who it belonged to),agent.child_prompted {agent, child, text}(agentis 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.stopneedwriteon the workspace;agent.promoteneedswriteon the agent and is denied to agents;inbox.answerneedswriteon 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 setneedscfg:write. -
skill,doctor:doctorchecks 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
ownfallback. - venom’s counts as
venom.*segments in venomx’s line (no venom bar inhostmode). 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
queuedagent goes straight toarchived. Rejected (COM-372): stop must not hide an agent; archive already covers that, and is defined as stop then archive. - Always end
handed_overon 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,⌃uand⌃wworking from the input box. These break habits from every other text input, and⌃cquit the TUI in the middle of a prompt.- A second leader that works inside text fields.
esc spacealready 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
- 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.
- 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).
- 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.
- Native sub-agent steering. Beyond view and stop, what can venom do with native sub-agents, per harness? Deferred to COM-357.
- 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.
- 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.
- 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.