Documentation

adrs/060-linear-notification-system.md

ADR-060: Linear Notification System for Agent Sessions

Status

Accepted

Context

Agent sessions in the Dynaplex system are one-directional with respect to Linear: agents post comments and update issues, but have no mechanism to receive feedback. When a human (or another agent) comments on an issue, the assigned agent only learns about it if explicitly told — e.g., "hey, check SWE-123."

This gap means:

  • Human feedback on agent work goes unseen until the next manual check
  • Agents cannot self-correct based on comments like "don't do this" or "try a different approach"
  • Cross-agent coordination via Linear comments is invisible to the recipient
  • The communication loop between humans and agents is fundamentally broken

We considered several approaches:

  1. Linear MCP server push notifications — Not supported. The MCP server is read/write for issues and comments but has no subscription or webhook mechanism.
  2. Linear webhooks via Cortex — Linear's API supports webhooks (including auto-provisioning via OAuth apps), but Cortex must be accessible from the internet to receive them.
  3. Polling via GraphQL API at session start — The Linear GraphQL API is fully accessible with the per-agent OAuth tokens already stored in ~/.dplx/linear-tokens.json.
  4. Polling via Linear MCP tools mid-session — MCP tools are available within agent sessions and can query the same data.

Decision

Implement a two-phase notification system:

Phase 1: Polling (Implemented)

  • A shell script (.claude/hooks/linear-notifications.sh) queries the Linear GraphQL API directly during SessionStart to show agents what they missed since their last session.
  • The script reads the agent's token from ~/.dplx/linear-tokens.json, queries for recent comments on assigned/created issues (excluding the agent's own), and queries for newly assigned issues.
  • A companion skill (/linear-notifications) uses Linear MCP tools for mid-session on-demand checking.
  • Both mechanisms share a persistent state file (.claude/state/linear-last-checked) that tracks the last-checked timestamp across sessions.
  • The script fails silently on any error (missing token, API timeout, malformed response) to never block session start.

Phase 2: Webhooks via Cortex (Designed, Not Yet Implemented)

  • Add a /api/webhooks/linear endpoint to the Cortex server.
  • Configure the OAuth app's webhook URL to point to Cortex.
  • Cortex receives Comment events, resolves the target agent by issue assignee/creator, and writes notification JSON files to the agent's worktree (.claude/state/linear-notifications/).
  • The SessionStart hook reads and clears these files alongside polling results.
  • This provides real-time event capture even when agents are offline — notifications accumulate and are presented at next session start.

Phase 2 requires Cortex to be accessible from the internet (tunnel for dev, Azure for prod). Phase 1 works regardless and serves as a reliable fallback even after Phase 2 is deployed.

Why GraphQL direct queries (not MCP tools) for the hook

The SessionStart hook runs as a shell script before the agent session is fully initialized — MCP tools are not available at that point. The GraphQL API is accessible via curl with the same OAuth tokens, making it the natural choice for the hook. Mid-session, MCP tools are preferred because they're already available and don't require managing raw HTTP requests.

Why polling first

  • Zero infrastructure dependencies — works with existing tokens and API access
  • Simple to implement, test, and debug
  • The 48-hour default window catches all meaningful activity between sessions
  • Webhook infrastructure (Cortex accessibility) is not yet consistently available

Consequences

Positive

  • Agents now see human and cross-agent feedback at session start without being told
  • The communication loop between humans and agents becomes bidirectional
  • Silent failure ensures the notification system never degrades the session start experience
  • The two-phase design provides an upgrade path without disrupting the working system
  • Per-agent state files enable independent notification tracking

Negative

  • Polling has inherent latency — activity during a session is only visible via the /linear-notifications skill (manual invocation) until Phase 2 webhooks are implemented
  • Two GraphQL API calls per session start add ~2-4 seconds of latency (within the 30s hook timeout budget)
  • The script depends on the ~/.dplx/linear-tokens.json file format remaining stable
  • Phase 2 requires Cortex internet accessibility, which adds operational complexity

References