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
The claude-code module gets task status into the Coder dashboard by asking the model to produce it. When report_tasks = true (the default), three things happen:
A ~25-line system prompt is injected via CODER_MCP_CLAUDE_SYSTEM_PROMPT instructing Claude to call coder_report_task(state, summary) on every state change.
coder exp mcp configure claude-code writes the same prompt plus the admin's system_prompt into ~/.claude/CLAUDE.md, so it appears twice in context.
The coder MCP server is registered with the full toolsdk surface (~25 tools: CreateWorkspace, DeleteTemplate, UploadTarFile, etc.). AllowedTools filters which can be called, but all ~25 schemas are still rendered into the system prompt.
AIGOV-93 reports the cost on a stock dogfood template: /context shows 61k/200k tokens consumed before any user input, a pwd task burns ~68k input tokens, and a 69-line code change costs ~2.5M tokens in ~7 minutes. Conservatively 12k to 20k of the per-turn overhead is the status-reporting machinery alone.
The signal is also non-deterministic. Status only updates if the model remembers to call the tool. When it forgets, the Tasks UI shows "working..." forever; when it over-reports, every micro-step becomes a tool round-trip.
#861 removes report_tasks and the system-prompt injection from this module, which is the right direction. This RFC proposes what should replace it.
Proposal
Claude Code already emits the lifecycle signals Tasks needs, deterministically, with zero context cost:
CC hook event
Fires when
Maps to Tasks state
SessionStart
session begins
working (initial)
Stop
agent finishes a turn
complete (or failure if last result has is_error)
SubagentStop
subagent finishes
(heartbeat)
Notification
agent needs user attention (permission prompt, idle)
failure (per current semantics: "needs user input")
Hooks support a type: "http" handler that POSTs the hook payload as JSON to a local URL with no shell spawn. The design:
**In whatever module owns Tasks orchestration post-**#861 (or in this module if #861 does not land):
Written to /etc/claude-code/managed-settings.d/10-coder-status.json at install time (composes with #863, the managed_settings PR).
Incoder/agentapi: add POST /internal/hook (loopback-only) that maps the hook payload to the existing app-status API. AgentAPI already holds CODER_AGENT_TOKEN and already forwards coder_report_task MCP calls to the same sink, so this is a sibling route, not new plumbing.
Incoder/coder (cli/exp_mcp.go):
Add --no-claude-md flag to exp mcp configure claude-code so it stops appending to ~/.claude/CLAUDE.md.
Add --tool <name> flag to exp mcp server; default to report_task only when CODER_MCP_APP_STATUS_SLUG is set. This drops the in-task MCP context cost from ~15k tokens to ~150 and closes a minor lateral-movement surface (the in-task agent can currently reach CreateWorkspace/DeleteTemplate with the owner's identity via other MCP clients).
working heartbeat: until #705 (agentapi v3 / SDK mode) lands, keep coder_report_task available as the single MCP tool so the model can post intermediate progress. Once AgentAPI consumes --output-format stream-json, derive working from assistant text blocks directly and the MCP tool becomes optional.
Ship behind report_tasks_mode = "hooks" | "legacy" (default legacy in the first release, flip to hooks after 2 to 4 weeks of opt-in). legacy is byte-identical to v4.x for rollback.
Dependencies
coder/coder: exp mcp configure --no-claude-md, exp mcp server --tool with in-task default
coder/agentapi: POST /internal/hook receiver
This repo: managed_settings plumbing (#863) so the hook config has somewhere to land
Open questions
Does the Stop hook payload include the final assistant message text directly, or only transcript_path? If only the path, AgentAPI tails the JSONL for the summary. (I can confirm this on the Anthropic side and update.)
chore(claude-code)!: strip boundary, agentapi, tasks, tools #861 pushes "starting Claude" to the caller. Where should the hooks drop-in be written from: this module's install (so it's present regardless of who starts Claude), or a new claude-code-tasks companion module?
Disclosure: I work at Anthropic on the Claude Code team. Filing this as an RFC rather than a PR because#861 *removes the surface a PR would patch; happy to turn it into code once the post-*#861module shape is settled.
Problem
The
claude-codemodule gets task status into the Coder dashboard by asking the model to produce it. Whenreport_tasks = true(the default), three things happen:CODER_MCP_CLAUDE_SYSTEM_PROMPTinstructing Claude to callcoder_report_task(state, summary)on every state change.coder exp mcp configure claude-codewrites the same prompt plus the admin'ssystem_promptinto~/.claude/CLAUDE.md, so it appears twice in context.coderMCP server is registered with the full toolsdk surface (~25 tools:CreateWorkspace,DeleteTemplate,UploadTarFile, etc.).AllowedToolsfilters which can be called, but all ~25 schemas are still rendered into the system prompt.AIGOV-93 reports the cost on a stock dogfood template:
/contextshows 61k/200k tokens consumed before any user input, apwdtask burns ~68k input tokens, and a 69-line code change costs ~2.5M tokens in ~7 minutes. Conservatively 12k to 20k of the per-turn overhead is the status-reporting machinery alone.The signal is also non-deterministic. Status only updates if the model remembers to call the tool. When it forgets, the Tasks UI shows "working..." forever; when it over-reports, every micro-step becomes a tool round-trip.
#861 removes
report_tasksand the system-prompt injection from this module, which is the right direction. This RFC proposes what should replace it.Proposal
Claude Code already emits the lifecycle signals Tasks needs, deterministically, with zero context cost:
SessionStartworking(initial)Stopcomplete(orfailureif last result hasis_error)SubagentStopNotificationfailure(per current semantics: "needs user input")Hooks support a
type: "http"handler that POSTs the hook payload as JSON to a local URL with no shell spawn. The design:**In whatever module owns Tasks orchestration post-**#861 (or in this module if #861 does not land):
Written to
/etc/claude-code/managed-settings.d/10-coder-status.jsonat install time (composes with #863, themanaged_settingsPR).In
coder/agentapi: addPOST /internal/hook(loopback-only) that maps the hook payload to the existing app-status API. AgentAPI already holdsCODER_AGENT_TOKENand already forwardscoder_report_taskMCP calls to the same sink, so this is a sibling route, not new plumbing.In
coder/coder(cli/exp_mcp.go):--no-claude-mdflag toexp mcp configure claude-codeso it stops appending to~/.claude/CLAUDE.md.--tool <name>flag toexp mcp server; default toreport_taskonly whenCODER_MCP_APP_STATUS_SLUGis set. This drops the in-task MCP context cost from ~15k tokens to ~150 and closes a minor lateral-movement surface (the in-task agent can currently reachCreateWorkspace/DeleteTemplatewith the owner's identity via other MCP clients).workingheartbeat: until #705 (agentapi v3 / SDK mode) lands, keepcoder_report_taskavailable as the single MCP tool so the model can post intermediate progress. Once AgentAPI consumes--output-format stream-json, deriveworkingfrom assistant text blocks directly and the MCP tool becomes optional.Token delta
report_tasks=true)<system>Migration
Ship behind
report_tasks_mode = "hooks" | "legacy"(defaultlegacyin the first release, flip tohooksafter 2 to 4 weeks of opt-in).legacyis byte-identical to v4.x for rollback.Dependencies
exp mcp configure --no-claude-md,exp mcp server --toolwith in-task defaultPOST /internal/hookreceivermanaged_settingsplumbing (#863) so the hook config has somewhere to landOpen questions
Stophook payload include the final assistant message text directly, or onlytranscript_path? If only the path, AgentAPI tails the JSONL for the summary. (I can confirm this on the Anthropic side and update.)claude-code-taskscompanion module?References: AIGOV-93, #284, #861, #705.
Disclosure: I work at Anthropic on the Claude Code team. Filing this as an RFC rather than a PR because #861 *removes the surface a PR would patch; happy to turn it into code once the post-*#861 module shape is settled.