The gap
A Repo Coder can start work and cannot observe it. Its resolved tool set (FAS platform
Coder 964594b6):
declared: repo_tree, repo_read_file, repo_git, repo_remote,
github_list_issues, github_read_issue, github_create_issue
+ BASE : memory, tasks, fetch_url, get_activity, run_pipeline, start_work, behaviour…
surfaceOptions.coding.drive === false correctly removes the CODING group
(list_coding_repos, read_terminal, send_to_cli) — a chat driving the engine would be the
second uncoordinated driver #154 exists to prevent. Correct decision.
But nothing replaced the read half. So after calling start_work the agent has:
- no way to read the engine's terminal (
read_terminal removed with the drive tools)
- no way to query the run it just started —
start_work returns a run id, and there is no tool
that takes one. GET /v1/instances/:id/loop/:runId exists for the console; the agent has no
equivalent.
- no way to list its own coding sessions (
list_coding_repos also went with the group)
get_activity is the only observability it has, and it is a generic event feed, not an answer to
"did the thing I started finish?"
What that causes
This is the mechanism behind #254. Real conversation, 2026-08-06:
start_work → Loop → Pilot ran git pull. It worked.
- User: "Did agent pull it or you pull it?"
- Agent: "I did not pull anything. I have no ability to run shell commands."
Faced with a direct challenge, it had no tool that could have settled the question. It could
not check the run, could not read the terminal, could not see the session. All it had was a system
prompt saying it cannot act. With no evidence, it deferred to the prompt and denied real work.
An agent that can act but cannot observe its own actions is structurally forced to either
fabricate or deny. Both were observed on this instance within two days.
Proposal
A read-only counterpart to start_work, granted wherever start_work is:
check_work(runId?) — status, iteration, outcome, detail for a run this instance started;
with no argument, the most recent. Backed by getLoopRun / listLoopRuns, which already exist.
- Restore a read-only terminal for
drive:false agents. Reading is not driving; the reason to
remove send_to_cli does not apply to read_terminal, and removing both was the overcorrection.
- Consider surfacing the last outcome in the context block automatically, so the agent does not
need a tool call to answer "what happened" about work it started this turn.
Verification
- After
start_work, the agent can answer "did that finish?" from a tool result rather than memory.
- Asked "did you do that?", it cites the run id and outcome.
- It still cannot send input to the engine —
drive:false is unchanged.
The gap
A Repo Coder can start work and cannot observe it. Its resolved tool set (FAS platform
Coder
964594b6):surfaceOptions.coding.drive === falsecorrectly removes theCODINGgroup(
list_coding_repos,read_terminal,send_to_cli) — a chat driving the engine would be thesecond uncoordinated driver #154 exists to prevent. Correct decision.
But nothing replaced the read half. So after calling
start_workthe agent has:read_terminalremoved with the drive tools)start_workreturns a run id, and there is no toolthat takes one.
GET /v1/instances/:id/loop/:runIdexists for the console; the agent has noequivalent.
list_coding_reposalso went with the group)get_activityis the only observability it has, and it is a generic event feed, not an answer to"did the thing I started finish?"
What that causes
This is the mechanism behind #254. Real conversation, 2026-08-06:
start_work→ Loop → Pilot rangit pull. It worked.Faced with a direct challenge, it had no tool that could have settled the question. It could
not check the run, could not read the terminal, could not see the session. All it had was a system
prompt saying it cannot act. With no evidence, it deferred to the prompt and denied real work.
An agent that can act but cannot observe its own actions is structurally forced to either
fabricate or deny. Both were observed on this instance within two days.
Proposal
A read-only counterpart to
start_work, granted whereverstart_workis:check_work(runId?)— status, iteration, outcome, detail for a run this instance started;with no argument, the most recent. Backed by
getLoopRun/listLoopRuns, which already exist.drive:falseagents. Reading is not driving; the reason toremove
send_to_clidoes not apply toread_terminal, and removing both was the overcorrection.need a tool call to answer "what happened" about work it started this turn.
Verification
start_work, the agent can answer "did that finish?" from a tool result rather than memory.drive:falseis unchanged.