Documentation
fsds/linear-notification-system.md
Feature Specification: Linear Notification System
Overview
The Linear notification system enables bidirectional communication between humans and agents via Linear. Agents automatically see recent comments, feedback, and new assignments when they start a session, and can check for updates mid-session via a skill.
Status
Version: 1.0
Status: Phase 1 Complete
Components: Claude Code hooks, skills, state management
Linear Issue: SWE-369
ADR: ADR-060: Linear Notification System
Architecture
Phase 1: Polling (Current)
Session Start Mid-Session
───────────── ───────────
session-start.sh /linear-notifications skill
│ │
▼ ▼
linear-notifications.sh Linear MCP tools
│ (list_issues, list_comments)
▼ │
Linear GraphQL API │
(curl + OAuth token) │
│ │
▼ ▼
Formatted markdown Formatted markdown
in session greeting shown to agent
│ │
└────────────┐ ┌────────────────┘
▼ ▼
.claude/state/linear-last-checked
(shared persistent timestamp)
Phase 2: Webhooks (Future)
Linear (Cloud) Cortex Server Agent Session
────────────── ────────────── ──────────────
Comment event ──────► /api/webhooks/linear session-start.sh
│ │
▼ ▼
Resolve agent Read notification
by assignee/creator files + poll API
│ │
▼ ▼
Write JSON to Formatted markdown
.claude/state/ in session greeting
linear-notifications/
Components
linear-notifications.sh (Hook Script)
Location: .claude/hooks/linear-notifications.sh
Invoked by: session-start.sh during SessionStart event
Standalone shell script that:
- Reads agent token from
~/.dplx/linear-tokens.json - Reads last-checked timestamp from
.claude/state/linear-last-checked - Queries Linear GraphQL API for viewer identity
- Queries for recent comments on assigned/created issues (excludes agent's own)
- Queries for newly assigned issues since last check
- Formats results as compact markdown
- Updates the last-checked timestamp
Arguments: <worktree-path> <agent-name>
Output: Markdown string (empty on no activity or error)
Exit code: Always 0 (silent failure by design)
session-start.sh Integration
Location: .claude/hooks/session-start.sh
Changes: Calls linear-notifications.sh and includes output in the session greeting
The notification output appears after the collaborators section, giving agents immediate visibility into what they missed.
/linear-notifications Skill
Location: .claude/skills/linear-notifications/SKILL.md
Type: User-invocable
Instructs the agent to use Linear MCP tools (list_issues, list_comments) to check for activity since the last-checked timestamp. Uses the same state file as the hook script.
State File
Location: .claude/state/linear-last-checked
Format: ISO-8601 UTC timestamp (e.g., 2026-02-26T21:37:15Z)
Lifecycle: Created on first run, updated after each successful check. NOT cleaned up by agent-signoff.sh — persists across sessions.
Data Flow
GraphQL Queries
Viewer identification:
{ viewer { id name } }
Recent activity (comments + new assignments):
{
recentComments: comments(
first: 25, orderBy: createdAt
filter: {
createdAt: { gte: "$SINCE" }
user: { id: { neq: "$VIEWER_ID" } }
issue: { or: [
{ assignee: { id: { eq: "$VIEWER_ID" } } }
{ creator: { id: { eq: "$VIEWER_ID" } } }
]}
}
) {
nodes { body createdAt user { name } issue { identifier title } }
}
viewer {
assignedIssues(
first: 10
filter: {
createdAt: { gte: "$SINCE" }
state: { type: { nin: ["completed", "canceled"] } }
}
) {
nodes { identifier title state { name } priority createdAt }
}
}
}
Output Format
## Linear Notifications
**3 new comment(s) on 2 issue(s) since Feb 25, 14:30 UTC**
### SWE-284: Complete Catalog shadow entity conversion
- [Feb 26, 16:22] **Daniel Castonguay**: I don't think we need to do anything else...
- [Feb 26, 16:29] **Bliss**: Agreed — the status gates/scopes approach is solid...
### SWE-331: Define container autoscaling policies
- [Feb 26, 19:18] **Daniel Castonguay**: We need more info about our situation before...
### Newly Assigned Issues
- **SWE-345**: Implement spatial zone hierarchy API (Normal priority)
Error Handling
The notification system is designed to be invisible on failure:
| Scenario | Behavior |
|---|---|
Missing ~/.dplx/linear-tokens.json |
Silent exit, no output |
| Agent not found in token file | Silent exit, no output |
| Linear API timeout (>10s) | Silent exit, no output |
| Malformed API response | Silent exit, no output |
| No new activity | Updates timestamp, no output |
| First run (no state file) | Defaults to 48-hour lookback window |
The session-start.sh integration wraps the call in 2>/dev/null with an || LINEAR_NOTIF="" fallback, ensuring the script can never block or break session start.
Configuration
| Setting | Location | Default |
|---|---|---|
| Agent tokens | ~/.dplx/linear-tokens.json |
N/A (required) |
| Last-checked timestamp | .claude/state/linear-last-checked |
48 hours ago |
| API timeout | linear-notifications.sh (CURL_TIMEOUT) |
10 seconds |
| Max comments fetched | GraphQL first parameter |
25 |
| Max new issues fetched | GraphQL first parameter |
10 |
| Comment body truncation | Script formatting logic | 200 characters |
Testing
Manual Script Testing
# Normal run (should show formatted output if there's activity)
.claude/hooks/linear-notifications.sh "$(pwd)" "AgentName"
# Verify silent exit on missing token
.claude/hooks/linear-notifications.sh "$(pwd)" "NonexistentAgent"
# Verify state file persistence
cat .claude/state/linear-last-checked
# Test with wider lookback window
echo "2026-02-01T00:00:00Z" > .claude/state/linear-last-checked
.claude/hooks/linear-notifications.sh "$(pwd)" "AgentName"
Session Integration Testing
- Start a new agent session via
dplx session new - Verify the notification section appears in the session greeting
- Leave a comment on an assigned issue from the Linear UI
- Start another session and verify the comment appears
Skill Testing
- Start an agent session
- Run
/linear-notifications - Verify it shows recent activity (or "no new activity")
- Verify
.claude/state/linear-last-checkedis updated
Future Work (Phase 2)
Phase 2 adds a webhook receiver in the Cortex server for real-time event capture:
- Webhook endpoint:
POST /api/webhooks/linearin Cortex - OAuth webhook toggle: Enable auto-webhook on the per-agent OAuth apps
- Event routing: Cortex resolves target agent(s) by issue assignee/creator
- File-based notifications: Write JSON files to
{worktree}/.claude/state/linear-notifications/ - Session pickup:
SessionStarthook reads and clears these files
This requires Cortex to be accessible from the internet. Until then, Phase 1 polling provides reliable coverage.
Related
- ADR-060: Linear Notification System
- SWE-369: Implement Linear notification system for agent sessions