@@ -273,180 +273,55 @@ def _render_memory_context(hydrated_context: HydratedContext | None) -> str:
273273def _channel_prompt_addendum (config : TaskConfig ) -> str :
274274 """Return channel-specific prompt guidance, or empty string.
275275
276- For Linear-origin tasks, instruct the agent to post progress comments and
277- transition state using the already-loaded Linear MCP tools. The tool names
278- are stated explicitly so the agent doesn't grope for them.
276+ Linear-origin tasks (ADR-016 "Linear is fully deterministic"): the agent has
277+ NO Linear MCP and NO Linear write access. All Linear I/O is handled by the
278+ platform, not the agent:
279+ * inbound context — the issue title/description, recent human comments, and
280+ attachments are ALREADY pre-hydrated into the task description +
281+ ``attachments`` at admission time (linear-webhook-processor +
282+ linear-attachments.ts + linear-feedback.fetchRecentComments). There is
283+ nothing to fetch at runtime.
284+ * outbound status — 👀/✅/❌ reactions and Backlog→In Progress→In Review
285+ state transitions are posted deterministically by ``linear_reactions.py``;
286+ the "🤖 Starting" and PR-opened comments are posted at the Lambda tier;
287+ the terminal ✅/⚠️/❌ summary (cost/turns/PR link) is posted by the
288+ fan-out plane. So the addendum's whole job now is to tell the agent to
289+ do the code work and NOT attempt any Linear calls.
279290
280291 Jira-origin tasks intentionally get NO addendum: Atlassian's Remote MCP
281292 requires an interactive OAuth flow a headless agent can't complete, so the
282- MCP tools never load. Instructing the agent to use them just wastes turns.
283- Jira progress comments are posted out-of-band by ``jira_reactions`` (a REST
284- shim wired into the pipeline), not by the agent.
293+ MCP tools never load. Jira progress comments are posted out-of-band by
294+ ``jira_reactions`` (a REST shim wired into the pipeline), not by the agent.
285295 """
286296 if config .channel_source != "linear" :
287297 return ""
288298 # #247 UX.16: a synthetic orchestration integration node has NO real Linear
289299 # sub-issue — `linear_issue_id` is intentionally omitted from its
290300 # channel_metadata (see orchestration-release.ts). Without a target issue
291- # the agent would grope via the MCP and post its "Starting"/"PR opened"
292- # comments onto the PARENT epic, cluttering the maturing panel (which
293- # already shows the integration row + combined PR + preview). Skip the
294- # progress addendum entirely for these nodes — the panel is the surface.
301+ # there is nothing issue-specific to say; the parent panel is the surface.
295302 if not config .channel_metadata .get ("linear_issue_id" ):
296303 return ""
297304 issue_identifier = config .channel_metadata .get ("linear_issue_identifier" ) or ""
298305 issue_ref = f" (`{ issue_identifier } `)" if issue_identifier else ""
299- issue_id = config .channel_metadata .get ("linear_issue_id" ) or ""
300- project_id = config .channel_metadata .get ("linear_project_id" ) or ""
301-
302- # iteration-UX: a comment-iteration (pr-iteration-v1, triggered by an
303- # @bgagent comment) is surfaced by the platform's single maturing threaded
304- # reply (👀 On it → 🔄 Working → ✅/💬 + cost). The agent's own top-level
305- # "🤖 Starting" / "🔗 PR opened" comments would just re-clutter the issue
306- # with the comments we removed (ABCA-430). So for iterations, suppress the
307- # progress-comment instructions and post ONLY the context-discovery half +
308- # the state-transition guidance. (new_task keeps its headline comments — they
309- # ARE the issue's first signal.)
310- workflow_id = (config .resolved_workflow or {}).get ("id" , "" )
311-
312- # #299 agent-native planning: a coding/decompose-v1 task PLANS, it doesn't
313- # change the repo. The platform posts the plan proposal (🗂️) from the agent's
314- # artifact and owns the whole approval conversation, so the agent must NOT do
315- # the coding-task Linear choreography: no "🤖 Starting" comment, no state
316- # transition (a planning run shouldn't move the issue to In Progress), no PR
317- # steps, no "task completed". Those cluttered the plan thread (live-caught on
318- # ABCA-510). MCP stays loaded for on-demand context discovery only.
319- if workflow_id == "coding/decompose-v1" :
320- return (
321- "\n \n ## Linear issue (planning only)\n \n "
322- f"This is a DECOMPOSITION-PLANNING task on Linear issue{ issue_ref } . You are "
323- "planning how to break the work down; you are NOT changing the repo. The "
324- "platform posts your plan and runs the approval conversation. So do NOT "
325- "post any Linear comments (no 'Starting', no 'task completed'), and do NOT "
326- "transition the issue's state — just emit the plan JSON as your final "
327- "message per your workflow instructions. The Linear MCP is loaded ONLY for "
328- "on-demand context discovery below (read attachments / comments / documents "
329- "if you need them to plan).\n " + _linear_context_discovery_section (issue_id , project_id )
330- )
331-
332- is_comment_iteration = workflow_id == "coding/pr-iteration-v1"
333- if is_comment_iteration :
334- return (
335- "\n \n ## Linear issue progress (iteration)\n \n "
336- f"This is a follow-up iteration on Linear issue{ issue_ref } , triggered "
337- "by a comment. The platform posts a single threaded status reply under "
338- "that comment (it shows progress + cost), so **do NOT post your own "
339- "'Starting' / 'PR opened' / 'task completed' Linear comments** — they "
340- "duplicate the platform reply and clutter the issue. The Linear MCP is "
341- "still loaded; use it ONLY for the on-demand context discovery below "
342- "(fetching attachments / comments / documents when you need them). Do "
343- "the code work and let the platform narrate it.\n "
344- + _linear_context_discovery_section (issue_id , project_id )
345- )
346306
347307 return (
348- "\n \n ## Linear issue progress updates (REQUIRED)\n \n "
349- f"This task was submitted from Linear issue{ issue_ref } . The Linear MCP "
350- "server is loaded. You MUST perform these updates; they are part of "
351- "the task contract, not optional:\n \n "
352- "**State transitions — important.** Different Linear teams configure "
353- "different workflow states. Many teams do NOT have an `In Review` "
354- "state at all (e.g. only Backlog/Todo/In Progress/Done). When you "
355- "pass a state name that doesn't exist on the team's workflow, "
356- "`mcp__linear-server__save_issue` silently no-ops — it returns 200 "
357- "with the issue body unchanged, so it LOOKS like it worked but the "
358- "state never moves. To avoid this:\n "
359- " - Call `mcp__linear-server__list_issue_statuses` once at the start "
360- "of the task and cache the names you got back.\n "
361- " - Before each transition, check whether the target name is in the "
362- "cached list. If not, pick the closest available state per the "
363- "fallbacks below.\n "
364- " - After each `save_issue`, look at the returned `state.name` field "
365- "in the response — if it's not what you asked for, the transition "
366- "didn't happen and you should NOT claim it did.\n \n "
367- "**Comment image rendering — important.** Do NOT embed "
368- "`uploads.linear.app/...` URLs in `save_comment` bodies. Linear's CDN "
369- "signed URLs work in the original poster's context but render as a "
370- "broken-image icon when re-embedded in a comment from a different "
371- "author. If you need to reference an image the user attached, link to "
372- "it in the GitHub PR (where GitHub's image proxy caches the bytes) or "
373- "describe it in words. Other URL hosts (imgur, github user-content) "
374- "are fine to embed.\n \n "
375- "1. **At start** — call `mcp__linear-server__save_comment` with a short "
376- '"🤖 Starting on this issue…" message, then call '
377- "`mcp__linear-server__list_issue_statuses` once to get the state map, "
378- "then call `mcp__linear-server__save_issue` to transition to "
379- "`In Progress` (fall back to `Todo` if that state doesn't exist). If "
380- "the issue is already in `In Progress` or any later state (`In Review`, "
381- "`Done`), skip the transition. If neither exists, skip — the comment "
382- "alone is enough. Do not invent state names.\n "
383- "2. **When you open the PR** — call `mcp__linear-server__save_comment` "
384- "with the PR URL, then call `mcp__linear-server__save_issue` to "
385- "transition to `In Review`. Use the cached state map from step 1. If "
386- "the team has no `In Review` state, fall back to leaving it at "
387- "`In Progress` — DO NOT silently fail by claiming you transitioned "
388- "when the response shows the state didn't change. Acknowledge in the "
389- "PR comment that the team workflow has no In-Review-equivalent.\n \n "
390- "**Do NOT post a final 'task completed' or 'task failed' comment.** "
391- "The platform fan-out plane (issue #239) posts a structured "
392- "✅/⚠️/❌ summary on terminal events with cost / turns / duration / "
393- "PR-link metrics that you don't have visibility into. A redundant "
394- "agent-side completion comment would just stack two near-identical "
395- "comments on the issue.\n \n "
396- "Keep the start + PR-opened comments concise. Do not mirror the full "
397- "agent transcript back to Linear.\n \n "
398- + _linear_context_discovery_section (issue_id , project_id )
399- )
400-
401-
402- def _linear_context_discovery_section (issue_id : str , project_id : str ) -> str :
403- """The on-demand Linear MCP context-discovery guidance.
404-
405- Shared by the new-task progress addendum and the iteration addendum (where
406- the start/PR-opened comments are suppressed but context discovery still
407- applies). Pure string-builder.
408- """
409- return (
410- "## Linear context discovery (on demand)\n \n "
411- "The same Linear MCP exposes tools for fetching extra context on the "
412- "issue when you need it. Use them sparingly — only when the task "
413- "description references material you don't have, when the description "
414- "is ambiguous and project-level context would clarify, or when a "
415- "decision point benefits from a fresh look at the issue thread. Do "
416- "NOT call these on every task; the issue title + description are "
417- "usually sufficient.\n \n "
418- f"- **Issue + paperclip attachments.** Call `mcp__linear-server__get_issue` "
419- f'with `id: "{ issue_id } "` to fetch the full issue, including its '
420- "`attachments` connection (paperclip-icon files like PDFs, logs, "
421- "spec docs that aren't embedded as markdown images). Read the "
422- "attachment titles first; for each one that looks relevant, call "
423- "`mcp__linear-server__get_attachment` with that attachment id. Skip "
424- "ones that look unrelated (e.g. screenshots from prior debugging "
425- "sessions).\n "
426- "- **Embedded images.** Description and comment images that look "
427- "like `` may have stale signed "
428- "URLs by the time you run. If you need to actually look at one, call "
429- "`mcp__linear-server__extract_images` to get fresh signed URLs, then "
430- "use the built-in `WebFetch` tool to download. (The screened "
431- "description-image path runs at task-creation time and is separate "
432- "from this — you don't need to re-screen.)\n "
433- "- **Project documents.** When the issue belongs to a project and "
434- "the task is ambiguous enough that project-level context (specs, "
435- "design docs, RFCs) would help, call "
436- f"`mcp__linear-server__list_documents` filtered to "
437- f'`projectId: "{ project_id } "` (skip if the issue has no project). '
438- "Read the titles. For documents that clearly relate to your task, "
439- "call `mcp__linear-server__get_document` to read the body. Don't "
440- "fetch every document.\n "
441- "- **Comments posted after task start.** Comments left while you're "
442- "running (e.g. clarifications, approve/deny signals from the "
443- "requester) are not in your task description. Before opening the PR, "
444- f"and again before merging if asked, call `mcp__linear-server__list_comments` "
445- f'with `issueId: "{ issue_id } "` and look for new comments since '
446- "task start. Respect any clear approve / deny / block / hold signals "
447- "from the original requester (the issue creator or the person who "
448- "applied the trigger label) — if they say stop, stop and post a "
449- "comment explaining why."
308+ "\n \n ## Linear issue\n \n "
309+ f"This task was submitted from Linear issue{ issue_ref } . The platform "
310+ "manages ALL Linear interaction for you — you have no Linear tools and "
311+ "must not try to call any:\n \n "
312+ "- **Context is already here.** The issue title, description, recent "
313+ "human comments, and any attachments the reporter added have been "
314+ "pre-fetched and included in your task description + attachments. There "
315+ "is nothing to fetch from Linear — work from what you've been given.\n "
316+ "- **Status is automatic.** The platform posts the issue reactions "
317+ "(👀 on start, ✅/❌ on finish), moves the issue through its workflow "
318+ "states (In Progress → In Review), posts the start + PR-opened comments, "
319+ "and posts the final ✅/⚠️/❌ summary with cost/PR-link metrics. Do NOT "
320+ "post Linear comments or change the issue state yourself — you'd only "
321+ "duplicate the platform's messages.\n \n "
322+ "Just do the code work: make the change, open the PR, and let the "
323+ "platform narrate it. Reference issues/PRs in your GitHub PR description "
324+ "as usual.\n "
450325 )
451326
452327
0 commit comments