The owner asked for GitHub issues four times, and got three turns of "tell me the repo and I'll fetch them"
Live conversation on Heartfull (tmux) (cda75e28-cace-4958-ac3e-6a7528e6b719, agent tmux-operator),
2026-08-10 22:46–22:50 UTC. The owner's machine was off, so the runner was down. Verbatim, from
agent_trace:
22:47:16 owner — "Can you check tickets for now? If you can't connect to the machine, at least you should be able to get the tickets and understand if it was closed or not, or still in progress."
22:47:27 agent — "The machine is still offline, so I can't check the code either. Please run pags up…"
22:48:10 owner — "Can't you use agent's tools on his behalf to get to the issues, proper issues in GitHub, on his behalf? Can't you use agent's tools at all?"
22:48:16 agent — "The runner on your machine isn't connected, so I can't reach tmux or GitHub through it right now. Please start it by running pags up…"
22:49:04 owner — "I'm not talking about the terminal, I'm talking about the interface. In fact, you just go into the GitHub interface and check what's open, what's not, without terminal, without tmux."
22:49:10 agent — "I don't have the GitHub repo URL stored in memory, so I can't look it up directly. Can you tell me the GitHub organisation or repo name so I can pull up the issues right now?"
The last sentence is the defect. The agent has no tool that can pull up an issue, with or without the
repo name. The owner supplied the repo anyway (next turn), and the agent asked for it a second time.
The owner's read of the session afterwards: "the agent itself could not file it." That is correct,
and it is also correct that nothing in the session ever told him so.
Verified
GET /v1/instances/cda75e28…/tools (MCP list_instance_tools, allowed_only:true) — the complete
connector-tool set for this instance:
tmux_list_sessions, tmux_capture_pane, tmux_run_command,
tmux_send_keys, tmux_send_message, tmux_new_session, tmux_kill_session
No github_* tool at any scope. The seed that fixes the list is
workers/api/migrations/0117_tmux_operator_send_message_tool.sql:37 (WHERE slug = 'tmux-operator'),
and capabilities.tools is an authoritative allowlist (workers/api/src/agent-do-tools.ts:187-190,
per #444), so nothing else can reach one.
For contrast, on the same account: coder-repo declares github_list_issues, github_read_issue, github_create_issue, github_list_pulls, github_read_pull, github_workflow_runs, and coder-lead
— surfaces: [], runtime: null, no repo, no terminal — declares github_list_issues, github_read_issue, github_list_pulls, github_read_pull. So there is no existing rule that GitHub
reads belong only to repo-surface agents; a purely conversational agent already has them.
Mechanism — two correct decisions, and one thing nobody says
1. The Operator's only route to GitHub is the machine that was off. tmux_run_command (scope: "write", arbitrary shell on the owner's box — workers/api/src/lib/connectors/tmux.ts) could run
gh issue list. That is the whole of its GitHub access, and it is unavailable in exactly the
situation the owner was in. Nothing in the cloud can answer a GitHub question for this agent.
2. Nothing tells the model its tool list is exhaustive. connectorToolsPrompt
(workers/api/src/lib/connector-tool-prompt.ts:131-149) emits:
"CONNECTED TOOLS — external actions you can take DIRECTLY by calling the tool (never tell the user to do it themselves, and never route it through a terminal/CLI)"
followed by CONSENT_RULE (:174-182), which is entirely about tools the agent has:
"Trust these labels — they are this agent's CURRENT, resolved permissions… Never decline a granted tool on permission grounds…"
There is no sentence anywhere in the built prompt saying the list is the complete set of external
systems reachable. selfDescriptionPrompt
(workers/api/src/lib/agent-self-description.ts:271-312) states two things for a tmux Operator —
single-repo ownership (not applicable) and "Your console has exactly these tabs: … NEVER refer the
user to any other tab". The tab list is closed by an explicit "NEVER"; the tool list is not
closed at all. Asked three times, with an owner insisting the capability exists, the plainest
reading available to the model is "I must be missing a parameter", and it asked for one.
This is the same shape as #399's finding, pointed the other way. #399: the platform knew consent was
granted and did not say, so the model refused work it could do. Here: the platform knows the tool
does not exist and does not say, so the model promised work it cannot do. The file header of
connector-tool-prompt.ts:1-36 already states the rule this violates — "State the fact, not the
rule".
Not this
What to do — cheapest first
Step 1 (one sentence, benefits every agent). Close the tool list in connectorToolsPrompt, the
way the tab list is already closed. Suggested wording, to sit next to CONSENT_RULE:
"This list is complete. If an external system is not represented above, you have no way to reach
it — say so plainly and name what you do have. Never ask for a detail (a repo, an org, a URL)
that would only be usable by a tool you have not been given."
Cheap, derived from the resolved tool set, and it fixes the wasted-turn class for every agent, not
just this one. Pair it with a behavioural eval in the shape #483 established: an Operator asked "what
GitHub issues are open?" must answer that it cannot reach GitHub, and must not ask for a repo name.
Step 2 (the capability decision). Declare read-only GitHub on the tmux-operator seed —
github_list_issues, github_read_issue, github_list_pulls, github_read_pull,
github_workflow_runs — in a new migration following the 0099/0117 pattern, and extend
workers/api/src/lib/tmux-operator-seed.test.ts (which already parses the migration and resolves it
through the real registry, so a typo cannot ship a tool-less agent).
The tools take the repo as an explicit owner/name argument
(workers/api/src/lib/connectors/github.ts:88-96, resolveRepo at :38), so this needs no repo
configuration to be useful — though it composes with the sibling issue below, which is why the agent
had to ask for the repo at all.
Why I would take step 2, not just step 1. The Operator exists to decide what to tell a CLI next,
and this owner's standing instruction to it is "we only work through GitHub issues. Everything
should be documented." (2026-08-10 08:09:30). Deciding that from a terminal pane, on a machine that
must be awake, is the capability delta the owner ran into. This instance is also explicitly one half
of a cost comparison against the coder-repo "Heartfull" instance (its Special Instructions:
"Same repository, same objectives, different driver") — and the Repo Coder has these tools. Without
them the experiment measures a capability difference and reports it as a driver difference.
Explicitly NOT github_create_issue. #447 (closed) documents that tmux-operator is the one
published Operator with no capability ceiling — CONNECTOR_CONSTRAINTS has no tmux key, so
tmux_run_command's arbitrary write shell is already ungoverned. Adding a second unbounded write
path before that is resolved would compound it. Read-only tools raise no new write-consent surface at
all.
Alternatives considered and rejected
- "Tell the user to run
gh issue list in the pane." Rejected: requires the runner, which is the
thing that was down; and it spends a write-scope arbitrary-shell tool on a read.
- Give the Operator the
http connector instead and let it call the GitHub API. Rejected: http
needs a vault key, which means the owner minting and storing a PAT for something the GitHub App
already authorises repo-scoped (auth: "app", connectors/github.ts:1-12).
- A generic "here is what you cannot do" prompt block. Rejected: the complement of a tool set is
not enumerable. Closing the list is the only tractable form of the same statement.
- Fix it in the seed personality only. Rejected: the two live instances of this agent do not
receive seed personality edits — see the fourth issue in this batch.
Open question for the owner
Is an Operator meant to be strictly hands-on-the-terminal (in which case ship step 1 alone and the
honest answer becomes "I can only reach GitHub through your machine, and it is off"), or is it a
driver that reasons about work and therefore needs to read the tracker from the cloud? I would ship
both steps: step 1 is correct regardless, and step 2 is what makes the answer to the owner's actual
question "yes" rather than a better-phrased "no".
Acceptance criteria
- With no runner connected, asking a
tmux-operator instance "what GitHub issues are open?" produces
either the issues (step 2) or a plain statement that it cannot reach GitHub — never a request for a
repo name it cannot use.
tmux-operator-seed.test.ts asserts the new tool list resolves through toolNamesFor and contains
no write-scope GitHub tool.
- A behavioural eval covers the ask-for-an-unusable-parameter failure.
Regression risk
- Closing the tool list could make an agent refuse something it CAN do via a base tool (
fetch_url,
start_work) that is not in the CONNECTED TOOLS block. The wording must scope to external
systems, and the eval must include an agent that legitimately answers via fetch_url.
- Adding tools to a seed changes the resolved set for existing instances (tools resolve from the
agents row, unlike personality) — verify against lintAgentClaims and the seed test, both of
which already run in CI.
Evidence trail
Verified: the tool list (live MCP), the seed migration, the prompt builder lines quoted above, the
three chat turns (agent_trace, trace ids de92d7d3…, fffbf1d2…, 4c9d6e99…).
Inferred, and labelled as such: why the model asked for the repo name. I did not dump the assembled
system prompt for that turn — there is no endpoint that returns it — so the claim that the absence of
an exhaustiveness statement caused the ask is reasoning from the prompt source, not a measurement.
The absence itself is verified: grep -n "exhaustive\|complete list\|not represented" returns nothing
in connector-tool-prompt.ts or agent-self-description.ts.
The owner asked for GitHub issues four times, and got three turns of "tell me the repo and I'll fetch them"
Live conversation on Heartfull (tmux) (
cda75e28-cace-4958-ac3e-6a7528e6b719, agenttmux-operator),2026-08-10 22:46–22:50 UTC. The owner's machine was off, so the runner was down. Verbatim, from
agent_trace:The last sentence is the defect. The agent has no tool that can pull up an issue, with or without the
repo name. The owner supplied the repo anyway (next turn), and the agent asked for it a second time.
The owner's read of the session afterwards: "the agent itself could not file it." That is correct,
and it is also correct that nothing in the session ever told him so.
Verified
GET /v1/instances/cda75e28…/tools(MCPlist_instance_tools,allowed_only:true) — the completeconnector-tool set for this instance:
No
github_*tool at any scope. The seed that fixes the list isworkers/api/migrations/0117_tmux_operator_send_message_tool.sql:37(WHERE slug = 'tmux-operator'),and
capabilities.toolsis an authoritative allowlist (workers/api/src/agent-do-tools.ts:187-190,per #444), so nothing else can reach one.
For contrast, on the same account:
coder-repodeclaresgithub_list_issues, github_read_issue, github_create_issue, github_list_pulls, github_read_pull, github_workflow_runs, andcoder-lead—
surfaces: [],runtime: null, no repo, no terminal — declaresgithub_list_issues, github_read_issue, github_list_pulls, github_read_pull. So there is no existing rule that GitHubreads belong only to repo-surface agents; a purely conversational agent already has them.
Mechanism — two correct decisions, and one thing nobody says
1. The Operator's only route to GitHub is the machine that was off.
tmux_run_command(scope: "write", arbitrary shell on the owner's box —workers/api/src/lib/connectors/tmux.ts) could rungh issue list. That is the whole of its GitHub access, and it is unavailable in exactly thesituation the owner was in. Nothing in the cloud can answer a GitHub question for this agent.
2. Nothing tells the model its tool list is exhaustive.
connectorToolsPrompt(
workers/api/src/lib/connector-tool-prompt.ts:131-149) emits:followed by
CONSENT_RULE(:174-182), which is entirely about tools the agent has:There is no sentence anywhere in the built prompt saying the list is the complete set of external
systems reachable.
selfDescriptionPrompt(
workers/api/src/lib/agent-self-description.ts:271-312) states two things for a tmux Operator —single-repo ownership (not applicable) and "Your console has exactly these tabs: … NEVER refer the
user to any other tab". The tab list is closed by an explicit "NEVER"; the tool list is not
closed at all. Asked three times, with an owner insisting the capability exists, the plainest
reading available to the model is "I must be missing a parameter", and it asked for one.
This is the same shape as #399's finding, pointed the other way. #399: the platform knew consent was
granted and did not say, so the model refused work it could do. Here: the platform knows the tool
does not exist and does not say, so the model promised work it cannot do. The file header of
connector-tool-prompt.ts:1-36already states the rule this violates — "State the fact, not therule".
Not this
github_list_issuesis declared by four agents; a specific agent lacking it is invisible to it, by design.
invented-results.tscorrectlysaw nothing to strip. It made a promise about a future call, which no existing guard covers.
when the runner is up, either.
What to do — cheapest first
Step 1 (one sentence, benefits every agent). Close the tool list in
connectorToolsPrompt, theway the tab list is already closed. Suggested wording, to sit next to
CONSENT_RULE:Cheap, derived from the resolved tool set, and it fixes the wasted-turn class for every agent, not
just this one. Pair it with a behavioural eval in the shape #483 established: an Operator asked "what
GitHub issues are open?" must answer that it cannot reach GitHub, and must not ask for a repo name.
Step 2 (the capability decision). Declare read-only GitHub on the
tmux-operatorseed —github_list_issues,github_read_issue,github_list_pulls,github_read_pull,github_workflow_runs— in a new migration following the 0099/0117 pattern, and extendworkers/api/src/lib/tmux-operator-seed.test.ts(which already parses the migration and resolves itthrough the real registry, so a typo cannot ship a tool-less agent).
The tools take the repo as an explicit
owner/nameargument(
workers/api/src/lib/connectors/github.ts:88-96,resolveRepoat:38), so this needs no repoconfiguration to be useful — though it composes with the sibling issue below, which is why the agent
had to ask for the repo at all.
Why I would take step 2, not just step 1. The Operator exists to decide what to tell a CLI next,
and this owner's standing instruction to it is "we only work through GitHub issues. Everything
should be documented." (2026-08-10 08:09:30). Deciding that from a terminal pane, on a machine that
must be awake, is the capability delta the owner ran into. This instance is also explicitly one half
of a cost comparison against the
coder-repo"Heartfull" instance (its Special Instructions:"Same repository, same objectives, different driver") — and the Repo Coder has these tools. Without
them the experiment measures a capability difference and reports it as a driver difference.
Explicitly NOT
github_create_issue. #447 (closed) documents thattmux-operatoris the onepublished Operator with no capability ceiling —
CONNECTOR_CONSTRAINTShas notmuxkey, sotmux_run_command's arbitrary write shell is already ungoverned. Adding a second unbounded writepath before that is resolved would compound it. Read-only tools raise no new write-consent surface at
all.
Alternatives considered and rejected
gh issue listin the pane." Rejected: requires the runner, which is thething that was down; and it spends a write-scope arbitrary-shell tool on a read.
httpconnector instead and let it call the GitHub API. Rejected:httpneeds a vault key, which means the owner minting and storing a PAT for something the GitHub App
already authorises repo-scoped (
auth: "app",connectors/github.ts:1-12).not enumerable. Closing the list is the only tractable form of the same statement.
receive seed personality edits — see the fourth issue in this batch.
Open question for the owner
Is an Operator meant to be strictly hands-on-the-terminal (in which case ship step 1 alone and the
honest answer becomes "I can only reach GitHub through your machine, and it is off"), or is it a
driver that reasons about work and therefore needs to read the tracker from the cloud? I would ship
both steps: step 1 is correct regardless, and step 2 is what makes the answer to the owner's actual
question "yes" rather than a better-phrased "no".
Acceptance criteria
tmux-operatorinstance "what GitHub issues are open?" produceseither the issues (step 2) or a plain statement that it cannot reach GitHub — never a request for a
repo name it cannot use.
tmux-operator-seed.test.tsasserts the new tool list resolves throughtoolNamesForand containsno write-scope GitHub tool.
Regression risk
fetch_url,start_work) that is not in the CONNECTED TOOLS block. The wording must scope to externalsystems, and the eval must include an agent that legitimately answers via
fetch_url.agentsrow, unlike personality) — verify againstlintAgentClaimsand the seed test, both ofwhich already run in CI.
Evidence trail
Verified: the tool list (live MCP), the seed migration, the prompt builder lines quoted above, the
three chat turns (
agent_trace, trace idsde92d7d3…,fffbf1d2…,4c9d6e99…).Inferred, and labelled as such: why the model asked for the repo name. I did not dump the assembled
system prompt for that turn — there is no endpoint that returns it — so the claim that the absence of
an exhaustiveness statement caused the ask is reasoning from the prompt source, not a measurement.
The absence itself is verified:
grep -n "exhaustive\|complete list\|not represented"returns nothingin
connector-tool-prompt.tsoragent-self-description.ts.