The owner asked the Lead to file a GitHub issue, twice, and was told no
Live conversation on Coder Lead (5fab318d-2850-45a4-982c-958765c7261e, agent coder-lead),
2026-08-10. Verbatim from agent_trace (source=chat, trace d4b28c8b-03f0-4caf-99ad-2b6e1d81e530):
02:20:03 owner — "Okay, second one. I think we tried to fix it before and we couldn't. It still persists. When people join by event link in the drop-down instead of the actual event it shows just the event ID …"
02:20:03 agent — "Can't file the issues right now — Heartfull already has a run in progress. Two options: 1. Wait … 2. Stop the current run …"
02:20:44 owner — "Don't you have access to github? Can't you file it directly? Or can you ask agent to file it without doing it through the command line, but file it through the tools?"
02:20:45 agent — "You're right — I can't create GitHub issues directly (I only have read tools for GitHub). But I can delegate the goal of filing those two issues to the Heartfull agent right now, alongside its current …"
02:21:36 owner — "Yes, do that. Absolutely do it."
02:21:36 agent — ❌ delegate_goal → "heartfull/platform is already being worked on"
02:22:53 owner — "Just wait"
02:22:53 agent — ✅ write_memory → fact:pending issue:Heartfull:event link shows ID instead of event name
02:22:53 agent — "Saved. I'll file the issue on HeartFull-online/platform as soon as the current run finishes."
It never did. The bug report is still a memory key. Nothing schedules that promise, nothing
re-reads it when the run ends, and the owner asked again the next day —
2026-08-11 01:29:22 owner — "Yes, all agents should be running and doing some open issues by priority order… Check if you have access to their issues also, you should be able to at least read their issues and file them also in the GitHub."
— and got a delegation summary with the issue-filing request unanswered (chat.out
2026-08-11 01:29:48).
Verified — measured against production, this account, today
GET /v1/instances/5fab318d…/tools (session bearer), github connector rows:
github_workflow_runs read allowed=False reason=not_declared writeConsent=n/a
github_list_issues read allowed=True reason=ok writeConsent=n/a
github_read_issue read allowed=True reason=ok writeConsent=n/a
github_list_pulls read allowed=True reason=ok writeConsent=n/a
github_read_pull read allowed=True reason=ok writeConsent=n/a
github_create_issue write allowed=False reason=not_declared writeConsent=required
So the agent's sentence is exactly true, and the platform never says it: it holds four GitHub
reads and no write.
Mechanism
capabilities.tools is an AUTHORITATIVE allowlist, not an addition
(workers/api/src/agent-do-tools.ts:187-193):
if (declared?.length) {
set = new Set<string>(BASE);
for (const name of declared) if (CREATOR_SELECTABLE_TOOLS.has(name)) set.add(name);
}
coder-lead's declaration is set by workers/api/migrations/0108_declare_unreachable_registry_tools.sql
(the last migration to write it), and it lists ten names — github_create_issue is not one:
["list_subordinates","subordinate_status","delegate_goal","check_delegation",
"github_list_issues","github_read_issue","github_list_pulls","github_read_pull",
"transfer_conversation","set_direction"]
That is not an oversight of the #444 class: the guard #444 shipped
(workers/api/src/lib/tool-reachability.test.ts) asks "does SOME agent declare this tool?", and
coder-repo does declare github_create_issue, so the guard is green while this gap is live.
A per-agent gap is structurally invisible to it — worth stating in the fix, because it is the
second time this exact shape has cost a live conversation.
The read grant was a deliberate judgement, recorded in
workers/api/migrations/0101_coder_github_pull_tools.sql:38-42:
"coder-lead IS INCLUDED, and that is the one judgement call here… A Lead reads issues to decide
what to delegate… It widens what the Lead READS and nothing else."
So "reads only" was chosen, once, on the pull-request question — not on the filing question, which
had never been asked. The owner has now asked it twice.
Why the delegation fallback is not the answer
The Lead's substitute for filing is delegate_goal, and on 2026-08-10 it failed for a reason that
has nothing to do with the request: startWork's per-repo single-flight refused because
heartfull/platform was already being worked on. Filing a bug report and writing code are different
jobs with different concurrency; routing the first through the second means a report can only be
filed when the repo happens to be idle. That is what pushed a real defect into a memory key.
The Engine can of course run gh issue create, but that costs a Pilot run and an Engine session on
the owner's machine to write forty words into a form — see the sibling issue on the missing
issue-mutation tools.
What to do
1. Cheapest, ships alone — declare github_create_issue on coder-lead.
One migration, same shape as 0101/0107/0108 (json_set re-setting the FULL $.capabilities
object, one UPDATE, idempotent, WHERE slug='coder-lead'), appending github_create_issue to the
existing ten. Restate the object verbatim otherwise — 0108's own note explains that a whole-object
set drops anything it does not restate.
No code path, no new auth: the tool, its handler and its write-consent gate already exist
(workers/api/src/lib/connectors/github.ts:210-221; the App token is minted by
installationTokenForOwner, which is owner-scoped, so the Lead can only reach repos the owner's
GitHub App installation already covers).
Reachability: this lands on the LIVE instance with no re-subscribe. capabilitiesForInstance
(workers/api/src/lib/agent-capabilities.ts:524-541) JOINs the agents row at read time, and
agent-think.ts:208 resolves capabilities per turn from the same source. This is not the #496
class (identity copied into DO state at subscribe) — verified by reading both paths.
2. The owner must then grant github WRITE on this instance. Measured above:
writeConsent: "required" — so even once declared, the first call is refused until write access for
the github connector is granted for this instance (console → Settings → Connections). Put this in
the acceptance criteria explicitly; shipping (1) without (2) turns "I can't" into "refused", which
is worse.
3. Then delete the substitute. With a real filing tool, the Lead must stop writing a bug report
into write_memory and promising to file it later. subordinate_status already returns
repo.githubRepo per subordinate and its own description says it is "the only value a GitHub tool
will accept" (workers/api/src/lib/connectors/supervision.ts:477,582), so the Lead can resolve the
repo without asking. A prompt line in the supervision block — file it now; a promise to file later
is not a record — belongs with the grant.
Open question, owner's to decide — how far does the Lead's GitHub write go?
I would grant github_create_issue and stop there for now.
- A (recommended):
github_create_issue only. Answers exactly what he asked for twice. Filing
is intake, which is the Lead's job description ("Turn a cross-repository request into goals",
migration 0063); it is additive and reversible (an unwanted issue can be closed).
- B: also
github_workflow_runs. The Lead is asked "was it delivered?" constantly and today
answers from check_delegation, which reports what the run claimed. CI is the independent
check. Read-scoped, no consent surface. Cheap, and I would take it in the same migration if the
owner wants it — but it is a separate question from the denial above.
- C: the full issue-mutation set (close/comment/label/assign). Declined here on purpose: those
tools do not exist yet on the connector, and the Lead is not where I would introduce them. See the
sibling issue.
Rejected: auto-granting every read-or-write tool of a connector an agent already touches.
Already considered and rejected for good reason in 0101_coder_github_pull_tools.sql:18-21 — it
inverts what the allowlist is for and would widen every existing agent whenever a connector grows.
Acceptance criteria
Regression risk
- A whole-object
json_set that forgets a name. This is the known failure here: 0107 re-set the
Lead's entire $.capabilities and lost set_direction. Mitigated by the parity assertion above;
that assertion is the part that must not be skipped.
- A Lead that files instead of delegating. It now has a cheap write and could answer "fix X" by
opening a ticket rather than handing over a goal. The prompt line in (3) should say filing is for
recording, delegation is for doing.
- Blast radius of a GitHub write from a repo-less agent. Bounded by the same installation token
every other agent uses; the Lead can reach no repo the owner's installation does not already cover.
github_create_issue is the only non-destructive GitHub write in the connector — there is no
merge, no close, no delete.
The owner asked the Lead to file a GitHub issue, twice, and was told no
Live conversation on Coder Lead (
5fab318d-2850-45a4-982c-958765c7261e, agentcoder-lead),2026-08-10. Verbatim from
agent_trace(source=chat, traced4b28c8b-03f0-4caf-99ad-2b6e1d81e530):It never did. The bug report is still a memory key. Nothing schedules that promise, nothing
re-reads it when the run ends, and the owner asked again the next day —
— and got a delegation summary with the issue-filing request unanswered (
chat.out2026-08-11 01:29:48).
Verified — measured against production, this account, today
GET /v1/instances/5fab318d…/tools(session bearer), github connector rows:So the agent's sentence is exactly true, and the platform never says it: it holds four GitHub
reads and no write.
Mechanism
capabilities.toolsis an AUTHORITATIVE allowlist, not an addition(
workers/api/src/agent-do-tools.ts:187-193):coder-lead's declaration is set byworkers/api/migrations/0108_declare_unreachable_registry_tools.sql(the last migration to write it), and it lists ten names —
github_create_issueis not one:That is not an oversight of the #444 class: the guard #444 shipped
(
workers/api/src/lib/tool-reachability.test.ts) asks "does SOME agent declare this tool?", andcoder-repodoes declaregithub_create_issue, so the guard is green while this gap is live.A per-agent gap is structurally invisible to it — worth stating in the fix, because it is the
second time this exact shape has cost a live conversation.
The read grant was a deliberate judgement, recorded in
workers/api/migrations/0101_coder_github_pull_tools.sql:38-42:So "reads only" was chosen, once, on the pull-request question — not on the filing question, which
had never been asked. The owner has now asked it twice.
Why the delegation fallback is not the answer
The Lead's substitute for filing is
delegate_goal, and on 2026-08-10 it failed for a reason thathas nothing to do with the request:
startWork's per-repo single-flight refused becauseheartfull/platformwas already being worked on. Filing a bug report and writing code are differentjobs with different concurrency; routing the first through the second means a report can only be
filed when the repo happens to be idle. That is what pushed a real defect into a memory key.
The Engine can of course run
gh issue create, but that costs a Pilot run and an Engine session onthe owner's machine to write forty words into a form — see the sibling issue on the missing
issue-mutation tools.
What to do
1. Cheapest, ships alone — declare
github_create_issueoncoder-lead.One migration, same shape as 0101/0107/0108 (
json_setre-setting the FULL$.capabilitiesobject, one UPDATE, idempotent,
WHERE slug='coder-lead'), appendinggithub_create_issueto theexisting ten. Restate the object verbatim otherwise — 0108's own note explains that a whole-object
set drops anything it does not restate.
No code path, no new auth: the tool, its handler and its write-consent gate already exist
(
workers/api/src/lib/connectors/github.ts:210-221; the App token is minted byinstallationTokenForOwner, which is owner-scoped, so the Lead can only reach repos the owner'sGitHub App installation already covers).
Reachability: this lands on the LIVE instance with no re-subscribe.
capabilitiesForInstance(
workers/api/src/lib/agent-capabilities.ts:524-541) JOINs theagentsrow at read time, andagent-think.ts:208resolves capabilities per turn from the same source. This is not the #496class (identity copied into DO state at subscribe) — verified by reading both paths.
2. The owner must then grant github WRITE on this instance. Measured above:
writeConsent: "required"— so even once declared, the first call is refused until write access forthe
githubconnector is granted for this instance (console → Settings → Connections). Put this inthe acceptance criteria explicitly; shipping (1) without (2) turns "I can't" into "refused", which
is worse.
3. Then delete the substitute. With a real filing tool, the Lead must stop writing a bug report
into
write_memoryand promising to file it later.subordinate_statusalready returnsrepo.githubRepoper subordinate and its own description says it is "the only value a GitHub toolwill accept" (
workers/api/src/lib/connectors/supervision.ts:477,582), so the Lead can resolve therepo without asking. A prompt line in the supervision block — file it now; a promise to file later
is not a record — belongs with the grant.
Open question, owner's to decide — how far does the Lead's GitHub write go?
I would grant
github_create_issueand stop there for now.github_create_issueonly. Answers exactly what he asked for twice. Filingis intake, which is the Lead's job description ("Turn a cross-repository request into goals",
migration
0063); it is additive and reversible (an unwanted issue can be closed).github_workflow_runs. The Lead is asked "was it delivered?" constantly and todayanswers from
check_delegation, which reports what the run claimed. CI is the independentcheck. Read-scoped, no consent surface. Cheap, and I would take it in the same migration if the
owner wants it — but it is a separate question from the denial above.
tools do not exist yet on the connector, and the Lead is not where I would introduce them. See the
sibling issue.
Rejected: auto-granting every read-or-write tool of a connector an agent already touches.
Already considered and rejected for good reason in
0101_coder_github_pull_tools.sql:18-21— itinverts what the allowlist is for and would widen every existing agent whenever a connector grows.
Acceptance criteria
GET /v1/instances/5fab318d…/toolsreportsgithub_create_issue→allowed: true, reason: "ok".HeartFull-online/platform about X" opens a real issue and the reply carries its number and URL —
with no
delegate_goal, nostart_work, and no Engine session.lock that broke 2026-08-10 is not on this path).
coder-leadinstance gets it without re-subscribing (assert against alive instance, not a fresh one).
coder2-parity.test.tsasserts the name is present in the Lead's effective declaration, so thenext whole-object
json_setcannot drop it — that trap is documented in 0108's own header andhas already fired once (
set_direction, dropped by 0107).write_memory+ "I'll file it later".Regression risk
json_setthat forgets a name. This is the known failure here: 0107 re-set theLead's entire
$.capabilitiesand lostset_direction. Mitigated by the parity assertion above;that assertion is the part that must not be skipped.
opening a ticket rather than handing over a goal. The prompt line in (3) should say filing is for
recording, delegation is for doing.
every other agent uses; the Lead can reach no repo the owner's installation does not already cover.
github_create_issueis the only non-destructive GitHub write in the connector — there is nomerge, no close, no delete.