|
| 1 | +--- |
| 2 | +title: Adr 017 linear agent session interaction |
| 3 | +--- |
| 4 | + |
| 5 | +# ADR-017: Linear agent-session as a future interaction channel |
| 6 | + |
| 7 | +**Status:** proposed |
| 8 | +**Date:** 2026-06-17 |
| 9 | + |
| 10 | +## Context |
| 11 | + |
| 12 | +ABCA's Linear integration today triggers and reports work through a |
| 13 | +**hand-rolled comment protocol** layered on Linear's generic Issue/Comment |
| 14 | +webhooks: |
| 15 | + |
| 16 | +- **Trigger** — a string match on `@bgagent` in a `Comment` webhook body |
| 17 | + (`parseCommentTrigger`), plus a label-add on an issue to seed a #247 |
| 18 | + orchestration. |
| 19 | +- **Acknowledgement** — emoji reactions managed by hand (👀 on receipt → |
| 20 | + ✅/❌ on settle via `swapCommentReaction`/`swapIssueReaction`), threaded |
| 21 | + replies (`replyToComment`), and a single maturing "epic panel" comment |
| 22 | + edited in place (`upsertEpicPanel`). |
| 23 | + |
| 24 | +This protocol works and is now well-tested (see the #247 UX.1–23 series), |
| 25 | +but the comment seam has been the single richest source of edge-case bugs: |
| 26 | +reply `issueId` vs `parentId` rules, "parent comment must be top-level" |
| 27 | +threading, webhook-redelivery reply spam, self-trigger loops from our own |
| 28 | +`@bgagent` example text, and reaction/state flapping. Each was a |
| 29 | +consequence of bolting an agent protocol onto a human-comment surface. |
| 30 | + |
| 31 | +Linear now ships a first-class **Agents API** (agent-session model): |
| 32 | +delegate or @mention an installed agent app → a typed `AgentSessionEvent` |
| 33 | +webhook (`created`/`prompted`) → the agent emits typed **activities** |
| 34 | +(`thought` / `action` / `response` / `elicitation` / `error`) and Linear |
| 35 | +derives a native session **state** (`pending`/`active`/`awaitingInput`/ |
| 36 | +`error`/`complete`/`stale`) with a built-in "thinking"/activity UI. |
| 37 | + |
| 38 | +Two facts establish the starting point: |
| 39 | + |
| 40 | +1. **The auth migration is already done.** ABCA's OAuth flow |
| 41 | + (`cli/src/linear-oauth.ts`) requests |
| 42 | + `read write app:assignable app:mentionable` with `actor=app`. Verified |
| 43 | + live on `backgroundagent-dev` (2026-06-17): both deployed workspace |
| 44 | + tokens (`bgagent-linear-oauth-maguireb`, `…-demo-abca`) carry exactly |
| 45 | + that scope. **bgagent is already installed as an app actor** — it is |
| 46 | + assignable, mentionable, and delegatable today. No auth work is needed |
| 47 | + to adopt agent sessions. |
| 48 | +2. **Linear is an interaction layer, not compute.** Adopting agent sessions |
| 49 | + changes *how we are triggered* and *how status is shown*. All compute |
| 50 | + (clone, run the coding agent, build/test, open the PR) still runs on |
| 51 | + ABCA's own AgentCore Runtime + ECS. The switch offloads nothing to |
| 52 | + Linear and does not change the AWS architecture or cost model. |
| 53 | + |
| 54 | +## Decision |
| 55 | + |
| 56 | +**Adopt the Linear agent-session model as an ADDITIONAL, flag-gated |
| 57 | +trigger/ack channel once Linear marks the Agents API GA — not now, and not |
| 58 | +as a replacement for the comment path.** |
| 59 | + |
| 60 | +The orchestration **engine** is channel-agnostic by design (the #247 |
| 61 | +trigger-agnostic seams): graph discovery, the reconciler, the epic |
| 62 | +panel/rollup, base-branch stacking, and the cascade do not care how a task |
| 63 | +was triggered. Agent sessions slot in as a new front end to that engine, |
| 64 | +mapping cleanly onto what we already built: |
| 65 | + |
| 66 | +| ABCA today (hand-rolled) | Linear agent-session (native) | |
| 67 | +|-------------------------------------|-----------------------------------| |
| 68 | +| `@bgagent` string match in comment | `created` AgentSessionEvent (mention/delegate) | |
| 69 | +| 👀 reaction "on it" | `thought` activity | |
| 70 | +| 🤖 Starting / 🔗 PR opened | `action` activity (+ result) | |
| 71 | +| ✅ Updated / completion | `response` activity | |
| 72 | +| ❌ failure reply | `error` activity | |
| 73 | +| "reply with guidance" retry (UX.9) | `elicitation` + `prompted` webhook + conversation history | |
| 74 | +| panel header state (🔄/✅/⚠️) | session state (active/complete/error) | |
| 75 | + |
| 76 | +### Why a channel, not a rewrite |
| 77 | + |
| 78 | +- The win is **real but partial**: agent sessions retire the brittle |
| 79 | + *trigger + per-comment ack* seam (the bug class above), but Linear agent |
| 80 | + sessions are **per-issue delegations with no native cross-issue epic |
| 81 | + rollup**. The #247 parent-epic panel, fan-out integration node, dependency |
| 82 | + cascade, and base-branch stacking stay ABCA's responsibility either way — |
| 83 | + so roughly half of the recent bug classes (panel settle, cross-issue |
| 84 | + concurrency) are unaffected by the migration. |
| 85 | +- The Agents API is a **Developer Preview** (confirmed against |
| 86 | + `developers.linear.app`, 2026-06-17): "in active development… may change |
| 87 | + before GA." Ripping out a working, now-hardened comment path to depend on |
| 88 | + an unstable API is the wrong trade today. |
| 89 | +- Treating it as an additive channel behind a flag (per ADR-006) lets us |
| 90 | + reuse the channel-agnostic engine, run both paths side by side during |
| 91 | + evaluation, and revert via the flag if the Preview API shifts. |
| 92 | + |
| 93 | +## Consequences |
| 94 | + |
| 95 | +- **Positive:** removes the highest-friction seam (string-match trigger + |
| 96 | + hand-rolled threading/reactions); native progress UI; conversation-history |
| 97 | + retry replaces our bespoke loop; no auth work (already app-actor). |
| 98 | +- **Negative / risk:** Preview API churn; hard runtime constraints (webhook |
| 99 | + receiver must return within ~5s; an activity or external URL must be |
| 100 | + emitted within ~10s of `created` or the session is marked unresponsive) — |
| 101 | + ABCA's task spawn is async and slower than 10s, so the `created` handler |
| 102 | + must emit an immediate `thought` ack and hand off, exactly as the current |
| 103 | + processor 👀s then spawns. |
| 104 | +- **No-op surfaces:** the orchestration engine, panel/rollup renderer, |
| 105 | + reconciler, cascade, and base-branch logic are untouched by this decision. |
| 106 | + |
| 107 | +## Phasing |
| 108 | + |
| 109 | +1. **Now (this ADR):** record the decision; auth verified; do not build. |
| 110 | + Keep the hardened comment path as the sole Linear interaction channel. |
| 111 | +2. **When Linear GAs the Agents API:** spike a flag-gated `agent-session` |
| 112 | + trigger/ack adapter behind the existing channel-agnostic engine — |
| 113 | + `created`→seed/iterate, activities↔our ack states — running in parallel |
| 114 | + with the comment path on `backgroundagent-dev`. |
| 115 | +3. **After evaluation:** if the native path is strictly better, default the |
| 116 | + flag on and deprecate the `@bgagent` string-match trigger; keep the |
| 117 | + panel/rollup engine. |
| 118 | + |
| 119 | +## Out of scope (this ADR) |
| 120 | + |
| 121 | +- Any implementation. This is a direction + go/no-go record only. |
| 122 | +- Changes to the orchestration engine, OAuth/token storage (done, ADR-016 |
| 123 | + governs pluggable identity), or the Slack/Jira channels. |
| 124 | +- The Mode B planner (#299) — orthogonal. |
| 125 | + |
| 126 | +## References |
| 127 | + |
| 128 | +- `cli/src/linear-oauth.ts` — `actor=app`, `app:assignable`/`app:mentionable` |
| 129 | +- `cdk/src/handlers/linear-webhook-processor.ts` — current comment trigger + acks |
| 130 | +- ADR-006 (feature flags), ADR-015 (Jira integration), ADR-016 (pluggable identity and auth) |
| 131 | +- Linear Agents API — `https://linear.app/developers/agents`, |
| 132 | + `https://linear.app/developers/agent-interaction` (Developer Preview, fetched 2026-06-17) |
| 133 | +- #247 UX.16–23 — the comment-path bug classes this would retire |
0 commit comments