Skip to content

[bug] The Coder Lead cannot file a GitHub issue — asked directly, it says so, and the bug report ends up in a memory key nothing ever reads #506

Description

@serge-ivo

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_memoryfact: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

  • GET /v1/instances/5fab318d…/tools reports github_create_issueallowed: true, reason: "ok".
  • With github write consent granted on that instance, asking the Coder Lead "file an issue on
    HeartFull-online/platform about X" opens a real issue and the reply carries its number and URL —
    with no delegate_goal, no start_work, and no Engine session.
  • The same request while that repo's Repo Coder is mid-run still files the issue (the single-flight
    lock that broke 2026-08-10 is not on this path).
  • Every already-subscribed coder-lead instance gets it without re-subscribing (assert against a
    live instance, not a fresh one).
  • coder2-parity.test.ts asserts the name is present in the Lead's effective declaration, so the
    next whole-object json_set cannot drop it — that trap is documented in 0108's own header and
    has already fired once (set_direction, dropped by 0107).
  • The Lead never again answers a "file this" request with write_memory + "I'll file it later".

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2: correctnessReal defect, no live harm today — inert fields, miscounts, missing guardsbugSomething isn't workingcoderThe Coder wedge agent (#68) — Engine, Pilot, Co-pilot, Loop, OverseerconnectorsConnector + tool frameworkcoordinationAgent coordination / delegation primitives

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions