Skip to content

feat(linear): add Linear worker dispatch and trigger handlers#1100

Merged
zbigniewsobiecki merged 2 commits intodevfrom
feature/linear-worker-dispatch-trigger-handlers
Apr 14, 2026
Merged

feat(linear): add Linear worker dispatch and trigger handlers#1100
zbigniewsobiecki merged 2 commits intodevfrom
feature/linear-worker-dispatch-trigger-handlers

Conversation

@aaight
Copy link
Copy Markdown
Collaborator

@aaight aaight commented Apr 14, 2026

Summary

  • Adds LinearJobData interface and 'linear' case in dispatchJob() in src/worker-entry.ts
  • Creates src/triggers/linear/ module with status-changed, label-added, comment-mention trigger handlers
  • Creates src/triggers/linear/webhook-handler.ts delegating to processPMWebhook()
  • Creates src/triggers/linear/register.ts and registers in src/triggers/builtins.ts
  • Adds sendLinearReaction() no-op to src/router/reactions.ts with 'linear' case in sendAcknowledgeReaction()
  • extractLinearContext(), resolveLinearBotUserId(), postLinearAck()/deleteLinearAck() were already implemented in their respective files

Implementation Details

Trigger handlers follow exact JIRA/Trello patterns:

  • LinearStatusChangedTrigger — fires on action=update, type=Issue when updatedFrom.stateId is present; matches new stateId against LinearConfig.statuses (which stores state IDs, not names)
  • LinearReadyToProcessLabelTrigger — fires on action=create, type=IssueLabel; checks label name matches readyToProcess config, then maps current stateId to agent type
  • LinearCommentMentionTrigger — fires on action=create, type=Comment; checks bot user ID mention in comment body, resolves bot identity via Linear GraphQL viewer query

Worker dispatchLinearJobData matches the shape of LinearJob in src/router/queue.ts (already existed for router-side use). The dispatchJob() switch case calls processLinearWebhook().

Test plan

  • tests/unit/triggers/builtins.test.ts updated with Linear trigger mocks and count updated from 21 → 24
  • All 373 test files pass (7361 tests)
  • TypeScript compiles cleanly (npm run typecheck)
  • Lint passes (npm run lint)

Trello card: https://trello.com/c/69de7fc0bb516f8059fe911f

🤖 Generated with Claude Code

🕵️ claude-code · claude-sonnet-4-6 · run details

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 14, 2026

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Solid implementation that follows the established JIRA/Trello trigger patterns closely. No blocking issues — CI passes, types align, and the architectural approach is correct. Three should-fix items around code duplication and dead code.

Should Fix

  • src/triggers/linear/comment-mention.ts:83-107 — Bot identity resolution is duplicated. resolveLinearBotUserId() in src/router/bot-identity-resolvers.ts already resolves the bot's Linear user ID via the exact same viewer GraphQL query, with a 60s TTL BotIdentityCache. The trigger handler re-implements this logic inline with a raw fetch and no caching. While the worker-per-container model limits the performance impact, this is still duplicated logic that should use the existing function. If the display name is needed, the existing function could be extended rather than maintaining a second implementation.

  • src/triggers/linear/comment-mention.ts:26-32 — The hasMention function has dead code: the comment on line 30 says "Also check plain @username patterns" but then immediately returns false. The whole function reduces to body.includes(userId). Additionally, the JSDoc (line 23) says Linear uses @[username](user:userId) while the inline comment (line 27) says @[Display Name](userId) — these are inconsistent descriptions of the mention format. The body.includes(userId) approach is fine in practice (Linear IDs are UUIDs), but the dead code and conflicting docs should be cleaned up.

  • src/triggers/linear/types.ts:91STATUS_TO_AGENT is duplicated verbatim between Linear and JIRA types (src/triggers/jira/types.ts). This is a maintenance risk — if a new status-to-agent mapping is added, it must be updated in both places. Consider extracting to a shared constant in src/triggers/shared/.

🕵️ claude-code · claude-opus-4-6 · run details

- Extract STATUS_TO_AGENT to src/triggers/shared/status-to-agent.ts and
  re-export from jira/types.ts and linear/types.ts to eliminate duplication
- Replace inline Linear viewer query in comment-mention.ts with the existing
  resolveLinearBotUserId() from bot-identity-resolvers.ts (cached, no raw fetch)
- Clean up hasMention(): remove dead code branch and fix inconsistent JSDoc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

LGTM — Clean, well-structured addition of Linear trigger handlers that closely follows the established JIRA/Trello patterns. The refactoring of STATUS_TO_AGENT into src/triggers/shared/status-to-agent.ts is a good deduplication move, and the JIRA re-export maintains backward compatibility.

All three trigger handlers (status-changed, label-added, comment-mention) correctly implement the TriggerHandler interface with appropriate matches() guards and handle() logic. The worker dispatch, reaction no-op, and test updates are all consistent with existing patterns. CI is green.

Minor observations (not blocking):

  • The registration ordering comment in register.ts ("LinearCommentMentionTrigger must be registered before the status-changed trigger so it gets first crack at comment events") is technically unnecessary since comment events (type=Comment) and status change events (type=Issue) are mutually exclusive in matches(). The ordering is harmless but the comment could be misleading. This mirrors the same pattern from JIRA registration, so it's fine for consistency.
  • The LinearJobData interface in worker-entry.ts correctly mirrors the LinearJob interface in src/router/queue.ts. Both should ideally share a single definition, but this follows the existing pattern where router and worker have their own type declarations (same as Trello/GitHub/JIRA/Sentry).

🕵️ claude-code · claude-opus-4-6 · run details

@zbigniewsobiecki zbigniewsobiecki merged commit 8d20981 into dev Apr 14, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants