diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f56c68ae..dddbfd7e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -120,7 +120,7 @@ This is enforced by commitlint via lefthook pre-commit hooks.
See [CLAUDE.md](./CLAUDE.md) for a detailed architecture overview. Key directories:
- `src/router/` — Webhook receiver (enqueues jobs to Redis)
-- `src/triggers/` — Event handlers (Trello, GitHub, JIRA)
+- `src/triggers/` — Event handlers (Trello, JIRA, Linear, GitHub)
- `src/agents/` — AI agent implementations
- `src/gadgets/` — Tools agents can use
- `src/api/` — Dashboard API (tRPC)
diff --git a/README.md b/README.md
index 1dc3aadb..ab1eb33a 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[](LICENSE)
[](https://nodejs.org/)
-> **Cascade orchestrates AI agents (Claude Code, Codex, opencode, LLMist) across your workflows in GitHub, Trello, and Jira.**
+> **Cascade orchestrates AI agents (Claude Code, Codex, opencode, LLMist) across your workflows in GitHub, Trello, Jira, and Linear.**
Cascade is an open-source platform that automates the full software development lifecycle. Connect your PM tool and GitHub repository, and Cascade drives work items from plan to merge:
@@ -38,7 +38,7 @@ For the full setup walkthrough — projects, credentials, webhooks, and triggers
## ⚡ Features
-- **Multi-PM support** — Works with Trello and JIRA out of the box
+- **Multi-PM support** — Works with Trello, JIRA, and Linear out of the box
- **11 agent types** — Splitting, planning, implementation, review, debug, respond-to-review, respond-to-CI, and more
- **Dual-persona GitHub model** — Separate implementer and reviewer bot accounts to prevent feedback loops
- **Web dashboard + CLI** — Monitor runs, manage projects, configure triggers
@@ -151,7 +151,7 @@ All project-level credentials (GitHub tokens, PM keys, LLM API keys) are stored
**Dual-persona GitHub model** — Cascade uses two separate GitHub bot accounts per project (implementer and reviewer) to prevent feedback loops. The implementer writes code and creates PRs; the reviewer reviews and approves them.
-**Trigger system** — Events from Trello, JIRA, and GitHub webhooks are matched against registered `TriggerHandler` instances. Triggers are configured per-project in the database.
+**Trigger system** — Events from Trello, JIRA, Linear, and GitHub webhooks are matched against registered `TriggerHandler` instances. Triggers are configured per-project in the database.
**Agent engines** — Agents run through a shared execution lifecycle with a pluggable engine registry. Default engine is `claude-code` (Anthropic Claude Code SDK). Alternatives: `llmist` (supports OpenRouter, Anthropic, OpenAI), `codex` (OpenAI Codex CLI), `opencode` (OpenCode server).
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 6562a2ef..ddf52cd8 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -1,6 +1,6 @@
# CASCADE Architecture
-CASCADE is a PM-to-Code automation platform that connects project management tools (Trello, JIRA), source control (GitHub), and monitoring (Sentry) to AI-powered agents that autonomously implement features, review PRs, debug failures, and manage backlogs. Webhooks from external providers flow through a router, get queued in Redis, and are processed by ephemeral worker containers that run agents against cloned repositories.
+CASCADE is a PM-to-Code automation platform that connects project management tools (Trello, JIRA, Linear), source control (GitHub), and monitoring (Sentry) to AI-powered agents that autonomously implement features, review PRs, debug failures, and manage backlogs. Webhooks from external providers flow through a router, get queued in Redis, and are processed by ephemeral worker containers that run agents against cloned repositories.
> **Relationship to CLAUDE.md**: `CLAUDE.md` is the operational reference (commands, env vars, how-to). This document and its deep-dives cover the *system design* — how components fit together and why.
@@ -11,6 +11,7 @@ graph TB
subgraph External["External Providers"]
Trello
JIRA
+ Linear
GitHub
Sentry
end
@@ -30,6 +31,7 @@ graph TB
Trello -->|webhook| Router
JIRA -->|webhook| Router
+ Linear -->|webhook| Router
GitHub -->|webhook| Router
Sentry -->|webhook| Router
@@ -39,6 +41,7 @@ graph TB
Worker -->|PRs, comments| GitHub
Worker -->|status updates| Trello
Worker -->|status updates| JIRA
+ Worker -->|status updates| Linear
Router <--> DB
Worker <--> DB
@@ -65,7 +68,7 @@ The canonical path from webhook to pull request:
```mermaid
sequenceDiagram
- participant P as Provider
(Trello/GitHub/JIRA/Sentry)
+ participant P as Provider
(Trello/JIRA/Linear/GitHub/Sentry)
participant R as Router
participant Q as Redis/BullMQ
participant W as Worker
@@ -110,10 +113,11 @@ sequenceDiagram
| `src/backends/` | LLM execution engines: Claude Code, LLMist, Codex, OpenCode |
| `src/gadgets/` | Tool implementations agents use (file ops, PM, SCM, alerting, shell) |
| `src/integrations/` | Unified integration interfaces, registry, bootstrap |
-| `src/pm/` | PM abstraction layer: provider interface, Trello/JIRA adapters, lifecycle |
+| `src/pm/` | PM abstraction layer: provider interface, Trello/JIRA/Linear adapters, lifecycle |
| `src/github/` | GitHub API client, dual-persona model, PR operations |
| `src/trello/` | Trello API client |
| `src/jira/` | JIRA API client (jira.js wrapper) |
+| `src/linear/` | Linear GraphQL API client |
| `src/sentry/` | Sentry API client, alerting integration |
| `src/config/` | Configuration provider, caching, credential resolution, integration roles |
| `src/db/` | Drizzle ORM schema, repositories, migrations |
diff --git a/docs/architecture/01-services.md b/docs/architecture/01-services.md
index f2769834..0f46957b 100644
--- a/docs/architecture/01-services.md
+++ b/docs/architecture/01-services.md
@@ -48,6 +48,7 @@ The router is the webhook ingestion point. It receives HTTP POST requests from e
| `POST /trello/webhook` | Trello | HEAD/GET returns 200 for Trello's verification |
| `POST /github/webhook` | GitHub | Injects `X-GitHub-Event` header into payload |
| `POST /jira/webhook` | JIRA | HEAD/GET returns 200 for JIRA verification |
+| `POST /linear/webhook` | Linear | HMAC-SHA256 via `linear-signature` header |
| `POST /sentry/webhook/:projectId` | Sentry | Project ID in URL for unambiguous routing |
| `GET /health` | Internal | Queue stats, active worker count |
@@ -94,7 +95,7 @@ The router passes job data to workers via Docker container env vars:
| Variable | Purpose |
|----------|---------|
| `JOB_ID` | Unique job identifier |
-| `JOB_TYPE` | `trello`, `github`, `jira`, `sentry`, `manual-run`, `retry-run`, `debug-analysis` |
+| `JOB_TYPE` | `trello`, `github`, `jira`, `linear`, `sentry`, `manual-run`, `retry-run`, `debug-analysis` |
| `JOB_DATA` | JSON-encoded job payload |
| `CASCADE_CREDENTIAL_KEYS` | Comma-separated list of credential env var names |
| Individual credential vars | Pre-loaded project credentials (e.g., `GITHUB_TOKEN_IMPLEMENTER`) |
@@ -106,6 +107,7 @@ type JobData =
| TrelloJobData // Trello webhook payload
| GitHubJobData // GitHub webhook payload
| JiraJobData // JIRA webhook payload
+ | LinearJobData // Linear webhook payload
| SentryJobData // Sentry webhook payload
| ManualRunJobData // Dashboard-initiated run
| RetryRunJobData // Retry a failed run
diff --git a/docs/architecture/02-webhook-pipeline.md b/docs/architecture/02-webhook-pipeline.md
index dfd929ed..17cedd11 100644
--- a/docs/architecture/02-webhook-pipeline.md
+++ b/docs/architecture/02-webhook-pipeline.md
@@ -1,6 +1,6 @@
# Webhook Pipeline
-Webhooks from external providers (Trello, GitHub, JIRA, Sentry) are processed through a two-layer system: a **webhook handler factory** that handles HTTP concerns, and a **router platform adapter** that implements the business logic pipeline.
+Webhooks from external providers (Trello, JIRA, Linear, GitHub, Sentry) are processed through a two-layer system: a **webhook handler factory** that handles HTTP concerns, and a **router platform adapter** that implements the business logic pipeline.
## Webhook Handler Factory
@@ -16,7 +16,7 @@ Each webhook endpoint provides a `WebhookHandlerConfig`:
```typescript
interface WebhookHandlerConfig {
- source: string; // 'trello' | 'github' | 'jira' | 'sentry'
+ source: string; // 'trello' | 'github' | 'jira' | 'linear' | 'sentry'
parsePayload: (c: Context) => ParseResult;
verifySignature?: (ctx, rawBody, projectId?) => VerificationResult | null;
processWebhook: (payload, eventType?, headers?) => Promise;
@@ -37,6 +37,7 @@ The factory handles:
| `parseGitHubPayload()` | JSON or form-encoded body | `X-GitHub-Event` header |
| `parseTrelloPayload()` | JSON body | `action.type` field |
| `parseJiraPayload()` | JSON body | `webhookEvent` field |
+| `parseLinearPayload()` | JSON body | `type` field |
| `parseSentryPayload()` | JSON body | `Sentry-Hook-Resource` header |
## Platform Adapters
@@ -81,6 +82,7 @@ interface ParsedWebhookEvent {
| `TrelloRouterAdapter` | `src/router/adapters/trello.ts` | `boardId` |
| `GitHubRouterAdapter` | `src/router/adapters/github.ts` | `repoFullName` |
| `JiraRouterAdapter` | `src/router/adapters/jira.ts` | JIRA project key |
+| `LinearRouterAdapter` | `src/router/adapters/linear.ts` | Linear team ID |
| `SentryRouterAdapter` | `src/router/adapters/sentry.ts` | CASCADE `projectId` (from URL) |
## The 12-Step Pipeline
@@ -144,6 +146,7 @@ Each provider's verification function checks for a stored `webhook_secret` crede
| GitHub | `X-Hub-Signature-256` | HMAC-SHA256 |
| Trello | Custom verification | Trello-specific |
| JIRA | `X-Hub-Signature` | HMAC-SHA256 |
+| Linear | `linear-signature` | HMAC-SHA256 (hex, no prefix) |
| Sentry | `Sentry-Hook-Signature` | HMAC-SHA256 |
If no webhook secret is configured for a project, verification is skipped (returns `null`).
diff --git a/docs/architecture/03-trigger-system.md b/docs/architecture/03-trigger-system.md
index 54b2ba70..213448bb 100644
--- a/docs/architecture/03-trigger-system.md
+++ b/docs/architecture/03-trigger-system.md
@@ -39,7 +39,7 @@ interface TriggerHandler {
```typescript
interface TriggerContext {
project: ProjectConfig;
- source: TriggerSource; // 'trello' | 'github' | 'jira' | 'sentry'
+ source: TriggerSource; // 'trello' | 'github' | 'jira' | 'linear' | 'sentry'
payload: unknown; // Raw webhook payload
personaIdentities?: PersonaIdentities; // GitHub bot identities
}
@@ -64,12 +64,16 @@ interface TriggerResult {
## Built-in Triggers
-Registration happens in `src/triggers/builtins.ts`, which delegates to per-platform `register.ts` files:
+Registration happens in `src/triggers/builtins.ts`. PM providers (Trello, JIRA, Linear) contribute triggers via the manifest registry; SCM and alerting providers use their own `register.ts` functions:
```typescript
function registerBuiltInTriggers(registry: TriggerRegistry): void {
- registerTrelloTriggers(registry);
- registerJiraTriggers(registry);
+ // PM providers register via the manifest registry (spec 006/009 pattern)
+ for (const manifest of listPMProviders()) {
+ for (const handler of manifest.triggerHandlers) {
+ registry.register(handler);
+ }
+ }
registerGitHubTriggers(registry);
registerSentryTriggers(registry);
}
@@ -109,6 +113,14 @@ function registerBuiltInTriggers(registry: TriggerRegistry): void {
| `PrReadyToMergeTrigger` | PR approved + checks pass | PM status update (no agent) |
| `PrConflictDetectedTrigger` | Merge conflict on PR | `resolve-conflicts` |
+### Linear triggers (`src/triggers/linear/`)
+
+| Handler | Event | Agent |
+|---------|-------|-------|
+| `LinearCommentMentionTrigger` | Bot @mentioned in issue comment | `respond-to-planning-comment` |
+| `LinearStatusChangedTrigger` | Issue state transition | Per-status mapping |
+| `LinearReadyToProcessLabelTrigger` | "cascade-ready" label added | `splitting` |
+
### Sentry triggers (`src/triggers/sentry/`)
| Handler | Event | Agent |
diff --git a/docs/architecture/08-config-credentials.md b/docs/architecture/08-config-credentials.md
index 700548c5..0acffb24 100644
--- a/docs/architecture/08-config-credentials.md
+++ b/docs/architecture/08-config-credentials.md
@@ -16,13 +16,14 @@ The config provider loads project configuration from the database with in-memory
| `loadProjectConfigByBoardId(boardId)` | Trello board ID | `{ project, config }` |
| `loadProjectConfigByRepo(repo)` | GitHub `owner/repo` | `{ project, config }` |
| `loadProjectConfigByJiraProjectKey(key)` | JIRA project key | `{ project, config }` |
+| `loadProjectConfigByLinearTeamId(teamId)` | Linear team ID | `{ project, config }` |
| `loadProjectConfigById(id)` | CASCADE project ID | `{ project, config }` |
### Caching
`src/config/configCache.ts` — in-memory cache with TTL populated at service startup. Caches:
- Full config object
-- Per-project lookups by board ID, repo, JIRA key
+- Per-project lookups by board ID, repo, JIRA key, Linear team ID
- Invalidated on config writes (via tRPC mutations)
## Config Schema
@@ -104,6 +105,11 @@ await withTrelloCredentials({ apiKey, token }, async () => {
await withJiraCredentials({ email, apiToken, baseUrl }, async () => {
// All JIRA API calls use these credentials
});
+
+// Linear
+await withLinearCredentials({ apiKey }, async () => {
+ // All Linear API calls use these credentials
+});
```
## Credential Encryption
diff --git a/docs/cascade-directory.md b/docs/cascade-directory.md
index d875941e..cdabbb4f 100644
--- a/docs/cascade-directory.md
+++ b/docs/cascade-directory.md
@@ -155,6 +155,7 @@ FEATURE_FLAGS=new-parser,strict-validation
```
TRELLO_API_KEY, TRELLO_TOKEN, GITHUB_TOKEN,
+LINEAR_API_KEY, LINEAR_WEBHOOK_SECRET,
OPENROUTER_API_KEY, CASCADE_WORKSPACE_DIR,
CASCADE_LOCAL_MODE, CASCADE_INTERACTIVE, CONFIG_PATH,
PORT, LOG_LEVEL, LLMIST_LOG_FILE, LLMIST_LOG_TEE,
diff --git a/docs/getting-started.md b/docs/getting-started.md
index e678c8b3..79d38b22 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -264,6 +264,26 @@ node bin/cascade.js projects integration-set my-project \
--config '{"baseUrl":"https://yourorg.atlassian.net","projectKey":"PROJ","statuses":{"todo":"To Do","inProgress":"In Progress","inReview":"In Review"}}'
```
+### Linear
+
+1. Generate a **Personal API key** from https://linear.app/settings/api
+2. (Optional) Create a webhook in your Linear workspace settings and note the signing secret
+
+```bash
+# Store Linear credentials (project-scoped)
+node bin/cascade.js projects credentials-set my-project --key LINEAR_API_KEY --value lin_api_... --name "Linear API Key"
+
+# Optional: webhook secret for signature verification
+node bin/cascade.js projects credentials-set my-project --key LINEAR_WEBHOOK_SECRET --value ... --name "Linear Webhook Secret"
+
+# Configure the integration
+# teamId: your Linear team UUID (find it via Settings > API or the team URL)
+# statuses: map Cascade lifecycle stages to Linear workflow state IDs
+node bin/cascade.js projects integration-set my-project \
+ --category pm --provider linear \
+ --config '{"teamId":"TEAM_UUID","statuses":{"todo":"STATE_UUID","inProgress":"STATE_UUID","done":"STATE_UUID"},"labels":{"readyToProcess":"LABEL_UUID","processing":"LABEL_UUID"}}'
+```
+
---
## 9. Set Up Webhooks
@@ -285,7 +305,7 @@ node bin/cascade.js webhooks create my-project \
--callback-url https://your-tunnel.ngrok.io
```
-This creates webhooks on GitHub (and Trello if configured) pointing to your Router.
+This creates webhooks on GitHub (and Trello if configured) pointing to your Router. For Linear, create the webhook manually in your Linear workspace settings, pointing to `https://your-router-host/linear/webhook`.
---
@@ -323,7 +343,7 @@ node bin/cascade.js projects trigger-discover --agent implementation
## 11. Test It
-1. Create a card in your PM tool (Trello/Jira) with a clear description of what code change you want
+1. Create a card in your PM tool (Trello/Jira/Linear) with a clear description of what code change you want
2. Move it to the status that triggers the implementation agent (or add the "Ready to Process" label)
3. Watch the dashboard — a new run should appear within seconds
4. The agent clones your repo, writes code, and opens a pull request