You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[bug] A coder agent can open a GitHub issue and then never touch it — no comment, close, relabel or assign tool, so closing one ticket costs a whole Engine run #507
An agent can OPEN an issue and can never touch it again
The GitHub connector has one write tool. A Coder can file a ticket, and then cannot comment on it,
close it, relabel it or assign it — the three things that actually happen to a ticket after it
exists. The owner hit this directly:
23:02:54 owner — "…what's the current issue, I don't want you to spoil [start] a new issue, but you assign the issue to me, if it needs my input." 23:03:12 agent — "The open issues are noted. On issue #16 — I can't assign issues to you via my tools (no write access to issue assignment)."
That sentence is true. It is also the second time in two days he has been told a coder agent has
no GitHub write it needs (see #506 for the Lead's filing gap).
✅ start_work Started work on: Close GitHub issue #128 in HeartFull-online/platform with the
comment: "Not in scope — Hearts economy n…
Closing one issue with one sentence of comment consumed a Pilot run and an Engine session on the
owner's machine — because gh issue close is the only route. The same detour is visible on every
other Repo Coder in the same window, always as an Engine instruction rather than a tool call:
So the capability exists on the platform — it is just only reachable by spending a coding run on it,
which needs a machine running pags up, an engine login, and a repo checkout. The Coder Lead has
none of those, which is why it cannot do this at all.
Verified — the connector has six tools and one write
github_create_issue's params are repo, title, body, labels (:214-219) — no assignees, which is precisely the field the owner asked for. And labels is settable only at
creation: nothing can change a label afterwards.
Grepped for the absent names across the whole worker — github_comment, github_close, github_update_issue, github_assign: 0 hits. There is no handler, no def and no
half-built path.
What to do
One new tool and one extension, both on the existing manifest. No new auth, no new consent
surface, no new scope. The App installation token already carries issues: write — that is what
makes github_create_issue work today (createIssueHandler, github.ts:131-152, POST /repos/{repo}/issues), and POST …/issues/{n}/comments + PATCH …/issues/{n} use the same
permission. github write consent is already granted per-instance on every Repo Coder
(measured: writeConsent: "granted" on f8ddc272…), so these arrive gated exactly like the
create tool.
1. github_comment_issue — {repo, number, body}, scope: "write", POST /repos/{repo}/issues/{number}/comments. This is the one that closes the loop the transcripts show:
recording why something was closed is the whole of steps 14/17 above.
2. github_update_issue — {repo, number, state?, labels?, assignees?, title?, body?}, scope: "write", PATCH /repos/{repo}/issues/{number}. Covers close/reopen, relabel and assign in
one call, which is how GitHub models it. assignees answers the quoted denial verbatim.
Both must call invalidateIssuesCache(ctx.env, ctx.userId, repo) the way createIssueHandler:150
does, and for the reason written there — the agent's next github_list_issues would otherwise read
its own pre-write copy and conclude nothing happened.
3. Declare them. A migration in the 0101/0108 shape, appending to the FULL restated $.capabilities object, one UPDATE per slug: coder-repo (and coder) get both; coder-lead gets github_comment_issue only — see the open question. capabilitiesForInstance
(lib/agent-capabilities.ts:524-541) JOINs the agents row at read time, so every live instance
picks this up on its next turn with no re-subscribe.
4. Add both to coder2-parity.test.ts's expected declaration so the next whole-object json_set cannot silently drop them — the trap 0108's header documents and that already fired
once on set_direction.
Alternatives considered and rejected
Leave it to the Engine (gh). That is today's behaviour and it is what the evidence indicts:
a Pilot run + an Engine session to write one comment, unavailable entirely to any agent without a
runner. It also spends the owner's tokens on a REST call the platform can make itself.
A generic github_request passthrough. Rejected: http_request already exists for
"call any API as configuration" and was deliberately not given the App token; a passthrough on an
installation token is an unbounded write to every repo the installation covers.
Also add PR merge/close. Rejected, and not by me: github_list_pulls's own description says "Read-only; there is deliberately no merge tool (the repo's merge policy governs that)"
(github.ts:203). Issue state is not that decision and does not reopen it.
Adding assignees to github_create_issue and stopping there. Rejected: it fixes the quoted
sentence only for issues the agent opens itself, and the owner's ask was about Trigger payload mapping UI and config validation #16, which already
existed.
Open question, owner's to decide — should the Lead be able to close an issue?
I would give the Lead github_comment_issue and not github_update_issue.
A Lead that reports on work should be able to leave the record on the ticket ("delegated to FWS,
run 70fc6c23"). Closing is a claim that work is done, and the Lead learns that second-hand from check_delegation — which is exactly the field it has already been observed over-trusting. Let the
agent that did the work close the ticket. If the owner disagrees, the fix is one extra name in the
same migration.
Acceptance criteria
"Assign issue Trigger payload mapping UI and config validation #16 to me and label it high priority" on a Repo Coder does it in ONE tool call,
with no start_work and no Engine session — verified in agent_trace (a tool.call for github_update_issue, no start_work on that turn).
Both refuse cleanly with explainWriteConsent's sentence when github write consent is not
granted for that instance, and never silently no-op.
A github_list_issues immediately after either write reflects the change (cache invalidated).
tool-reachability.test.ts stays green — i.e. the migration lands in the same PR as the
connector change, not after it.
coder2-parity.test.ts names both tools in the expected declaration.
Regression risk
An agent closing issues it should not. The real one. github_update_issue lets a Loop-driven
agent tidy a backlog on its own initiative; state changes are reversible but noisy on a shared
repo. Mitigation: keep it off the Lead (above), and the write-consent gate stays the owner's
switch. A test asserting the tool is refused without consent is the one that catches a regression
here.
An agent can OPEN an issue and can never touch it again
The GitHub connector has one write tool. A Coder can file a ticket, and then cannot comment on it,
close it, relabel it or assign it — the three things that actually happen to a ticket after it
exists. The owner hit this directly:
Chess coder 2 (
bd43f4de-ef35-4051-bdec-43f8571414a1, agentcoder-repo), 2026-08-11:That sentence is true. It is also the second time in two days he has been told a coder agent has
no GitHub write it needs (see #506 for the Lead's filing gap).
What it costs when the agent works around it
Heartfull (
f8ddc272-0390-4826-8812-94989e3d2ebd),agent_trace2026-08-11 21:33:02:Closing one issue with one sentence of comment consumed a Pilot run and an Engine session on the
owner's machine — because
gh issue closeis the only route. The same detour is visible on everyother Repo Coder in the same window, always as an Engine instruction rather than a tool call:
e4d2d031…) Loop step 17, 02:15:20 — "First, post a comment on issue [connectors] Single tool registry — one definition → runtime + MCP + API #85 recording: (a) the correction made…"5219a03a…) Loop step 14, 02:17:20 — "Do the cleanup pass now. Close the completed issues with appropriate comments… Run"b849b4f6completion, 22:51:47 — "Issue CODER-008: Super agent (orchestrator chat) missing reliable delegation tool — cannot send tasks to per-repo coding Engines #119 closed with summary comment." (done by the Engine, viagh)So the capability exists on the platform — it is just only reachable by spending a coding run on it,
which needs a machine running
pags up, an engine login, and a repo checkout. The Coder Lead hasnone of those, which is why it cannot do this at all.
Verified — the connector has six tools and one write
workers/api/src/lib/connectors/github.ts,GITHUB_MANIFEST.tools(:154-222), complete:github_workflow_runs:160github_list_issues:170github_read_issue:181github_list_pulls:191github_read_pull:201github_create_issue:211github_create_issue's params arerepo,title,body,labels(:214-219) — noassignees, which is precisely the field the owner asked for. Andlabelsis settable only atcreation: nothing can change a label afterwards.
Grepped for the absent names across the whole worker —
github_comment,github_close,github_update_issue,github_assign: 0 hits. There is no handler, no def and nohalf-built path.
What to do
One new tool and one extension, both on the existing manifest. No new auth, no new consent
surface, no new scope. The App installation token already carries
issues: write— that is whatmakes
github_create_issuework today (createIssueHandler,github.ts:131-152,POST /repos/{repo}/issues), andPOST …/issues/{n}/comments+PATCH …/issues/{n}use the samepermission.
githubwrite consent is already granted per-instance on every Repo Coder(measured:
writeConsent: "granted"onf8ddc272…), so these arrive gated exactly like thecreate tool.
1.
github_comment_issue—{repo, number, body},scope: "write",POST /repos/{repo}/issues/{number}/comments. This is the one that closes the loop the transcripts show:recording why something was closed is the whole of steps 14/17 above.
2.
github_update_issue—{repo, number, state?, labels?, assignees?, title?, body?},scope: "write",PATCH /repos/{repo}/issues/{number}. Covers close/reopen, relabel and assign inone call, which is how GitHub models it.
assigneesanswers the quoted denial verbatim.Both must call
invalidateIssuesCache(ctx.env, ctx.userId, repo)the waycreateIssueHandler:150does, and for the reason written there — the agent's next
github_list_issueswould otherwise readits own pre-write copy and conclude nothing happened.
3. Declare them. A migration in the 0101/0108 shape, appending to the FULL restated
$.capabilitiesobject, one UPDATE per slug:coder-repo(andcoder) get both;coder-leadgetsgithub_comment_issueonly — see the open question.capabilitiesForInstance(
lib/agent-capabilities.ts:524-541) JOINs theagentsrow at read time, so every live instancepicks this up on its next turn with no re-subscribe.
4. Add both to
coder2-parity.test.ts's expected declaration so the next whole-objectjson_setcannot silently drop them — the trap0108's header documents and that already firedonce on
set_direction.Alternatives considered and rejected
gh). That is today's behaviour and it is what the evidence indicts:a Pilot run + an Engine session to write one comment, unavailable entirely to any agent without a
runner. It also spends the owner's tokens on a REST call the platform can make itself.
github_issue_actiontool with anactionenum. Rejected: it hides the write surfacebehind a string, so the write-consent gate and
/v1/instances/:id/toolscan no longer say whatthe agent may actually do to an issue — the transparency [connectors] Connector consent + write-scope safety + admin visibility #90/[bug] An agent refuses work it IS allowed to do — the prompt says every write tool "needs consent" and never says the consent is granted #399 exist for.
github_requestpassthrough. Rejected:http_requestalready exists for"call any API as configuration" and was deliberately not given the App token; a passthrough on an
installation token is an unbounded write to every repo the installation covers.
github_list_pulls's own description says"Read-only; there is deliberately no merge tool (the repo's merge policy governs that)"
(
github.ts:203). Issue state is not that decision and does not reopen it.assigneestogithub_create_issueand stopping there. Rejected: it fixes the quotedsentence only for issues the agent opens itself, and the owner's ask was about Trigger payload mapping UI and config validation #16, which already
existed.
Open question, owner's to decide — should the Lead be able to close an issue?
I would give the Lead
github_comment_issueand notgithub_update_issue.A Lead that reports on work should be able to leave the record on the ticket ("delegated to FWS,
run
70fc6c23"). Closing is a claim that work is done, and the Lead learns that second-hand fromcheck_delegation— which is exactly the field it has already been observed over-trusting. Let theagent that did the work close the ticket. If the owner disagrees, the fix is one extra name in the
same migration.
Acceptance criteria
with no
start_workand no Engine session — verified inagent_trace(atool.callforgithub_update_issue, nostart_workon that turn).call, no coding run.
explainWriteConsent's sentence when github write consent is notgranted for that instance, and never silently no-op.
github_list_issuesimmediately after either write reflects the change (cache invalidated).tool-reachability.test.tsstays green — i.e. the migration lands in the same PR as theconnector change, not after it.
coder2-parity.test.tsnames both tools in the expected declaration.Regression risk
github_update_issuelets a Loop-drivenagent tidy a backlog on its own initiative; state changes are reversible but noisy on a shared
repo. Mitigation: keep it off the Lead (above), and the write-consent gate stays the owner's
switch. A test asserting the tool is refused without consent is the one that catches a regression
here.
invalidateIssuesCachereproduces [feature] Coding agents cannot show a pull request — add a Pulls panel, and make "don't fetch if nothing changed" true with GitHub conditional requests #401's exact failure with adifferent verb. Assert it, as the create-path presumably already does.
json_setdropping a sibling name. Covered by criterion 6.