feat(linear): add Linear webhook ingestion in router#1099
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
nhopeatall
left a comment
There was a problem hiding this comment.
Summary
Clean, well-structured feature addition that follows established patterns closely (JIRA/Sentry adapters). CI is green. One should-fix item worth discussing before merge.
Architecture & Design
The Linear integration plugs in at the correct layer and follows the existing RouterPlatformAdapter contract faithfully. Pattern consistency with the JIRA adapter is excellent — the file structure, credential resolution, webhook verification, and ack comment flow are all directly analogous.
Code Issues
Should Fix
- src/router/adapters/linear.ts:98-101 —
isSelfAuthored()always returnsfalsewhileCommentis inPROCESSABLE_TYPES. When the bot posts an ack comment viaLinearPlatformClient.postComment(), Linear fires acreate/Commentwebhook back to this handler. SinceisSelfAuthorednever identifies the bot's own comments, the event proceeds through the pipeline. If any trigger matchescreate/Comment, this creates an infinite loop (bot posts comment → webhook → trigger → agent runs → bot posts comment → …). The JIRA adapter handles this correctly by resolving the bot's account ID and comparing it against the comment author. Even if no triggers currently match comment creation, this is a latent hazard — a single trigger config change could silently activate the loop. Consider either (a) implementinguserIdcomparison against the Linear API key owner, or (b) removingCommentfromPROCESSABLE_TYPESuntil self-authored detection is in place.
Observations (non-blocking)
- src/linear/types.ts vs runtime payloads —
LinearWebhookCommentDatadoesn't includeteamId, but the adapter accessesdata.teamIdon all event types (including Comment) via aRecord<string, unknown>cast. The test fixture includesteamIdin Comment payloads. If actual Linear Comment webhook payloads omitteamIdfromdata, comment events would be silently dropped (no project match). This may be intentional ("comments without team context are unprocessable"), but worth confirming against real Linear webhook deliveries.
🕵️ claude-code · claude-opus-4-6 · run details
Add resolveLinearBotUserId() using the Linear GraphQL viewer query, mirroring the JIRA bot identity pattern. isSelfAuthored() now compares the comment payload's userId against the bot's user ID so bot-posted ack comments are filtered before trigger dispatch, preventing an infinite create/Comment webhook loop when triggers match comment creation events. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
nhopeatall
left a comment
There was a problem hiding this comment.
Summary
LGTM — Clean, well-structured implementation that correctly follows the established JIRA/Trello adapter pattern for adding Linear webhook ingestion to the router.
The implementation touches all the right extension points: webhook parser, signature verification, router adapter, platform client, credential resolution, ack message generation, bot identity resolution, job queue type, and barrel exports. Each piece is consistent with how the other platforms (especially JIRA) are wired up.
Key verifications:
- Signature verification correctly uses HMAC-SHA256 with raw hex (no prefix), matching Linear's documented format, with timing-safe comparison via the shared
verifyHmachelper - GraphQL mutations use proper parameterized variables (no string interpolation)
- All error paths are gracefully handled with logging — no unhandled promise rejections
isSelfAuthoredproperly resolves the bot user via theviewerGraphQL query with caching- The
createWebhookVerifierfactory is reused correctly, maintaining the same null-skip-on-no-secret semantics as other platforms - Test coverage is thorough (24 adapter tests + 13 signature tests) with good edge case coverage
One minor observation (not blocking): LinearWebhookCommentData in src/linear/types.ts doesn't include teamId, but the adapter and signature verification both depend on data.teamId for Comment events. This works at runtime because the type union includes Record<string, unknown> as a catch-all, and the real Linear API does send teamId on all event types. Consider adding teamId to LinearWebhookCommentData in a follow-up to make the type more accurate.
🕵️ claude-code · claude-opus-4-6 · run details
Summary
/linear/webhookPOST route with GET verification handler insrc/router/index.tsLinearRouterAdapterinsrc/router/adapters/linear.tsfollowing the same pattern asJiraRouterAdapterLinearPlatformClientinsrc/router/platformClients/linear.tsfor posting/deleting/updating comments on Linear issues via GraphQL APIresolveLinearCredentials()tosrc/router/platformClients/credentials.tsand extendsresolveWebhookSecret()to handle'linear'providerLinearJobinterface tosrc/router/queue.tsand includes it in theCascadeJobunionparseLinearPayload()tosrc/webhook/webhookParsers.ts(re-exported fromwebhookHandlers.ts)verifyLinearWebhookSignature()andextractLinearTeamId()tosrc/router/webhookVerification.tsusing HMAC-SHA256 on theLinear-SignatureheaderextractLinearContext()tosrc/router/ackMessageGenerator.tsfor contextual ack messagespostLinearAck()/deleteLinearAck()tosrc/router/acknowledgments.tsLinearPlatformClientandresolveLinearCredentialsfromsrc/router/platformClients/index.tsKey decisions
sha256=prefix) matching Linear's documented formatextractLinearTeamId()reads fromdata.teamIdin the webhook payload, consistent with theLinearWebhookPayloadtypeLinearRouterAdapter.isSelfAuthored()always returnsfalse(no bot persona in Linear yet); can be enhanced laterLinearRouterAdapter.sendReaction()is a no-op (Linear does not support emoji reactions on issues via webhook)LinearPlatformClientuses the Linear GraphQL API withcommentCreate/commentDelete/commentUpdatemutationswebhook_secretcredential (same pattern as JIRA)Test plan
tests/unit/router/adapters/linear.test.ts— 24 tests covering allLinearRouterAdaptermethods (parseWebhook, isProcessableEvent, isSelfAuthored, sendReaction, resolveProject, dispatchWithCredentials, postAck, buildJob)tests/unit/router/webhook-signature.test.ts— added 13 tests forextractLinearTeamIdandverifyLinearWebhookSignatureCard
https://trello.com/c/ijLbHh21/595-as-a-developer-i-want-linear-webhook-ingestion-in-the-router-so-that-linear-events-are-received-verified-and-enqueued
🤖 Generated with Claude Code
🕵️ claude-code · claude-sonnet-4-6 · run details