Enrichment Agent: Architecture & Trust

17
Nodes (main)
11
Data Sources
3
Trigger Modes
3
Dedup Levels
2
Active Workflows

Workflow 1: Enrichment Agent

Linear Ticket Enrichment Agent v3.1 ActiveG3TTZLOOX56FOv42
Linear Trigger
Validate & Route
AI Agent (Claude 4.6)
Post Comment

11 tool nodes connected to AI Agent via ai_tool | Timezone: America/New_York | Model: claude-sonnet-4-6, temp 0.2

Trigger Modes

ModeTrigger ConditionEnrichment ScopeDedup WindowReport Tag
create New D2C ticket created (human actor only) Full protocol: all 11 sources, claim verification, follow-the-leads 1 hour (none)
status_in_progress Ticket moves to any started state + updatedFrom.stateId present Full protocol (same as create). Developer needs fresh context 7 calendar days (re-enrichment)
pr_linked Any update on ticket with GitHub PR in attachments Lightweight: PR details + brief Slack (2 queries) + related tickets only. Skips BQ, Sentry, Vercel, Drive, Notion, Figma 24 hours (lightweight)

11 Connected Data Sources

🐙
GitHub
PRs, commits, reviews
💬
Slack
Search, threads, users
Linear
Issues, comments, search
🐛
Sentry
Errors, stacktraces
Vercel
Deploys, build logs
📑
Notion
Wiki, docs, pages
📂
Google Drive
Specs, PRDs, docs
📊
BigQuery
Metrics, claim verify
🎨
Figma
Nodes, comments
📈
Langfuse
LLM traces, prompts
Statsig
Gates, experiments
Source routing: Base layer (GitHub, Slack, Linear, Drive, Notion) runs on EVERY ticket. Specialized layer (BQ, Sentry, Vercel, Figma, Langfuse, Statsig) activates by ticket type + content triggers. Lightweight mode skips specialized layer entirely.

3-Level Deduplication

Level 1
In-Memory Lock
globalThis lock per issueId. Blocks duplicate webhooks within 120 seconds. First line of defense, catches ~90% of duplicates instantly.
Level 2
Comment Age Check
Queries Linear comments in Validate node. Checks report age against trigger-specific window (1h / 7d / 24h). Prevents unnecessary AI Agent runs.
Level 3
Database Dedup
Post Comment node queries Linear before posting. Blocks if report posted <5 min ago. Catches race conditions from parallel n8n workers.

Workflow 2: Stale Ticket Detector

Stale Ticket Detector v1.0 ActiveJtmMZikcr83HiEi7
Daily Schedule (10 AM ET, weekdays)
Detect Stale Tickets
Post Health Check

Scans D2C "In Progress" tickets | Flags if no activity for 7+ days | Health Check dedup: 24 hours

ParameterValue
TargetD2C team tickets in started state type
Staleness threshold7 calendar days since last updatedAt
Health Check dedupSkip if ## Health Check comment posted < 24 hours ago
Comment includesDays stale, assignee, linked PR count, actionable suggestions
ScheduleWeekdays at 10:00 AM ET (cron: 0 10 * * 1-5)

Enrichment Report Structure

## Enrichment Report
Type: bug/feature/analytics/infra | Confidence: High/Medium/Low | Run: timestamp ET (re-enrichment)
### TL;DR
2-3 sentences. Lead with most important finding. Actionable direction.
### Code
PRs with size metrics, review status, author. One line each.
### Errors
Sentry issues (top 2 by event count). Bug type only.
### Discussions
Verified Slack messages. Grouped by topic if 4+.
### Docs
Google Drive + Notion docs. Content-verified before inclusion.
### Design
Figma nodes with variant summary. Feature type only.
### Claim Verification
Every claim: Confirmed / Contradicted / Unverified + concrete evidence.
### Related Tickets
Duplicates, prior art, parallel work, blockers.
### Open Questions
Max 1-2 genuinely unresolved items.

Validate Node Decision Tree

1. Extract issueId from webhook payload
2. Check in-memory lock (120s per issueId) → skip if locked
3. Determine action:
create → filter bot actors → triggerReason = "create"
update + state started → filter bot actors → triggerReason = "status_in_progress"
update + other → triggerReason = "_check_pr_linked"
other actionskip
4. Fetch full issue via Linear GraphQL
5. Filter: team must be D2C, no "skip-enrichment" label
6. Find age of most recent Enrichment Report comment
7. Apply trigger-specific dedup:
create: report < 1h → skip
status_in_progress: report < 7 days → skip
_check_pr_linked: no GitHub PR → skip | report < 24h → skip
8. Pass to AI Agent with issueData + triggerReason + ET timestamp

Production Validation

TestTicketResultDuration
create Full enrichmentD2C-4849Pass 3 PRs, Statsig gate, related tickets~85s
re-enrichmentD2C-4849Pass "(re-enrichment)" tag, fresh data~85s
pr_linked LightweightD2C-4766Pass PR #1138, 3 Slack, 3 related~36s
Dedup: fresh reportD2C-4849Pass In Progress <7d skipped<1s
Race condition fixPost CommentFixed 5-min DB dedupn/a
Figma integrationD2C-4828Pass URL parse, node, comments~56s

Required n8n Configuration

Variables (16)

VariableSourceUsed By
LINEAR_API_KEYLinear Settings > APIValidate, Linear API, Post Comment
LINEAR_OAUTH_CLIENT_IDLinear OAuth AppPost Comment (bot identity)
LINEAR_OAUTH_CLIENT_SECRETLinear OAuth AppPost Comment (bot identity)
GITHUB_TOKENGitHub PATGitHub API
SLACK_BOT_TOKENSlack App > OAuthSlack API
SENTRY_AUTH_TOKENSentry > Auth TokensSentry API
VERCEL_TOKENVercel > TokensVercel API
NOTION_API_KEYNotion IntegrationNotion API
GOOGLE_SA_CLIENT_EMAILGCP Service AccountDrive, BigQuery
GOOGLE_SA_PRIVATE_KEY_1GCP SA key (part 1)Drive, BigQuery
GOOGLE_SA_PRIVATE_KEY_2GCP SA key (part 2)Drive, BigQuery
GOOGLE_PROJECT_IDGCP ProjectBigQuery
FIGMA_API_TOKENFigma PATFigma API
LANGFUSE_PUBLIC_KEYLangfuse SettingsLangfuse API
LANGFUSE_SECRET_KEYLangfuse SettingsLangfuse API
STATSIG_API_KEYStatsig ConsoleStatsig API

Credentials (2)

CredentialTypeUsed By
Anthropic accountanthropicApiClaude Sonnet 4.6
Linear accountlinearApiWebhook trigger