{
  "id": "h5gumj6V5XNoMdBI",
  "name": "Triage GitHub Issues with AI Category and Embedding-Based Duplicate Detection",
  "tags": [],
  "nodes": [
    {
      "id": "745894c9-1ce4-4b32-8020-b8b961f09f12",
      "name": "GitHub Issue Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        80,
        368
      ],
      "parameters": {
        "path": "github-issue-triage",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "b01b74ef-9385-4aa7-a608-ddfbf0e59ad8",
      "name": "Set Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        288,
        368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-1",
              "name": "github_owner",
              "type": "string",
              "value": "YOUR_GITHUB_OWNER"
            },
            {
              "id": "cfg-2",
              "name": "github_repo",
              "type": "string",
              "value": "YOUR_REPO_NAME"
            },
            {
              "id": "cfg-3",
              "name": "duplicate_threshold",
              "type": "number",
              "value": 0.85
            },
            {
              "id": "cfg-4",
              "name": "roadmap_sheet_id",
              "type": "string",
              "value": "REPLACE_WITH_SHEET_ID"
            },
            {
              "id": "cfg-5",
              "name": "roadmap_sheet_name",
              "type": "string",
              "value": "feature_roadmap"
            },
            {
              "id": "cfg-6",
              "name": "dev_slack_channel",
              "type": "string",
              "value": "#dev-alerts"
            },
            {
              "id": "cfg-7",
              "name": "faq_url",
              "type": "string",
              "value": "https://your-docs.example.com/faq"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "186a9537-2b0a-4447-8b8e-42ca794e8308",
      "name": "Only On Issue Opened",
      "type": "n8n-nodes-base.if",
      "position": [
        480,
        368
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f-1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('GitHub Issue Webhook').item.json.body.action }}",
              "rightValue": "opened"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "20c02959-8b7d-4d80-a480-f30359e8c949",
      "name": "Extract Issue Info",
      "type": "n8n-nodes-base.set",
      "position": [
        704,
        368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ex-1",
              "name": "issue_number",
              "type": "number",
              "value": "={{ $('GitHub Issue Webhook').item.json.body.issue.number }}"
            },
            {
              "id": "ex-2",
              "name": "issue_title",
              "type": "string",
              "value": "={{ $('GitHub Issue Webhook').item.json.body.issue.title }}"
            },
            {
              "id": "ex-3",
              "name": "issue_body",
              "type": "string",
              "value": "={{ $('GitHub Issue Webhook').item.json.body.issue.body || '' }}"
            },
            {
              "id": "ex-4",
              "name": "issue_html_url",
              "type": "string",
              "value": "={{ $('GitHub Issue Webhook').item.json.body.issue.html_url }}"
            },
            {
              "id": "ex-5",
              "name": "issue_author",
              "type": "string",
              "value": "={{ $('GitHub Issue Webhook').item.json.body.issue.user.login }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3236b243-0009-487c-bc0b-4d2dd6174534",
      "name": "Fetch Recent Issues",
      "type": "n8n-nodes-base.github",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        944,
        368
      ],
      "parameters": {
        "limit": 30,
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.github_owner }}"
        },
        "resource": "repository",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.github_repo }}"
        },
        "authentication": "oAuth2",
        "getRepositoryIssuesFilters": {
          "state": "all"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "a0d01de3-e6e2-4a7c-8096-51776c4b56c7",
      "name": "Prepare Texts for Embedding",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        368
      ],
      "parameters": {
        "jsCode": "// ==========================================\n// Prepare text array for batch embeddings:\n// [new issue, past issue 1, past issue 2, ...]\n// ==========================================\nconst newIssue = $('Extract Issue Info').item.json;\nconst response = $input.first().json;\nconst pastIssues = Array.isArray(response) ? response : (response.body || []);\n\n// New issue text (first in array)\nconst texts = [\n  (newIssue.issue_title + '\\n' + (newIssue.issue_body || '')).substring(0, 2000)\n];\nconst metadata = [{ is_new: true }];\n\n// Past issues (exclude the new issue itself if present)\nfor (const issue of pastIssues) {\n  if (issue.number === newIssue.issue_number) continue;\n  if (issue.pull_request) continue; // skip PRs\n  texts.push(\n    (issue.title + '\\n' + (issue.body || '').substring(0, 500)).substring(0, 2000)\n  );\n  metadata.push({\n    is_new: false,\n    number: issue.number,\n    title: issue.title,\n    html_url: issue.html_url,\n    state: issue.state\n  });\n}\n\nreturn [{ json: { texts, metadata, new_issue: newIssue } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "6f566b9d-0c55-4a33-84fe-9c935a2ba79b",
      "name": "Get Embeddings (Batch)",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        1360,
        368
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/embeddings",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"text-embedding-3-small\",\n  \"input\": {{ JSON.stringify($json.texts) }}\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 2000
    },
    {
      "id": "601840c3-8d5e-45ce-8c50-01eb7a4a445d",
      "name": "Calculate Similarity",
      "type": "n8n-nodes-base.code",
      "position": [
        1616,
        368
      ],
      "parameters": {
        "jsCode": "// ==========================================\n// Cosine similarity: new issue vs each past issue\n// Find the max to detect duplicate\n// ==========================================\nfunction cosine(a, b) {\n  let dot = 0, magA = 0, magB = 0;\n  for (let i = 0; i < a.length; i++) {\n    dot += a[i] * b[i];\n    magA += a[i] * a[i];\n    magB += b[i] * b[i];\n  }\n  return dot / (Math.sqrt(magA) * Math.sqrt(magB));\n}\n\nconst embedResp = $input.first().json;\nconst embeddings = embedResp.data;\nconst metadata = $('Prepare Texts for Embedding').item.json.metadata;\nconst newIssue = $('Prepare Texts for Embedding').item.json.new_issue;\nconst threshold = $('Set Configuration').item.json.duplicate_threshold || 0.85;\n\nif (!embeddings || embeddings.length < 2) {\n  // No past issues to compare against\n  return [{\n    json: {\n      ...newIssue,\n      max_similarity: 0,\n      is_duplicate: false,\n      matched_issue: null\n    }\n  }];\n}\n\nconst newVec = embeddings[0].embedding;\nlet maxSim = 0;\nlet matched = null;\n\nfor (let i = 1; i < embeddings.length; i++) {\n  const sim = cosine(newVec, embeddings[i].embedding);\n  if (sim > maxSim) {\n    maxSim = sim;\n    matched = metadata[i];\n  }\n}\n\nreturn [{\n  json: {\n    ...newIssue,\n    max_similarity: Math.round(maxSim * 1000) / 1000,\n    is_duplicate: maxSim >= threshold,\n    matched_issue: matched\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "249beca5-71aa-4766-adb4-a93ad60bb38f",
      "name": "If Duplicate",
      "type": "n8n-nodes-base.if",
      "position": [
        1824,
        368
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "if-dup",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.is_duplicate }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "241741d1-8fc5-48d8-aa8a-b7bc889e218e",
      "name": "Post Duplicate Comment",
      "type": "n8n-nodes-base.github",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2112,
        304
      ],
      "parameters": {
        "body": "=\ud83d\udc4b Thanks for the report! This looks like a possible duplicate of a recent issue (similarity {{ $json.top_similarity }}). We'll close this one to keep things tidy \u2014 please comment on the original issue if this still affects you.",
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_owner }}"
        },
        "operation": "createComment",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_repo }}"
        },
        "issueNumber": "={{ $('Extract Issue Info').item.json.issue_number }}",
        "authentication": "oAuth2"
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "f1929589-7203-4fb7-ae9c-fcac82f263db",
      "name": "Close as Duplicate",
      "type": "n8n-nodes-base.github",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2384,
        304
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_owner }}"
        },
        "operation": "edit",
        "editFields": {
          "state": "closed",
          "labels": [
            "duplicate"
          ]
        },
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_repo }}"
        },
        "issueNumber": "={{ $('Extract Issue Info').item.json.issue_number }}",
        "authentication": "oAuth2"
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "c4acc737-3324-4429-9eb3-d6fd32cb8207",
      "name": "Classify Issue",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "onError": "continueRegularOutput",
      "maxTries": 2,
      "position": [
        2064,
        624
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "You are a GitHub issue triage assistant. Classify the issue into exactly ONE of these categories: \"bug\", \"feature\", \"question\", \"spam\".\n\nRules:\n- \"bug\": reports of broken functionality, errors, crashes, regressions.\n- \"feature\": requests for new functionality or enhancements.\n- \"question\": how-to questions, usage help, FAQ-style asks.\n- \"spam\": off-topic, advertising, nonsense, or clearly malicious content.\n\nRespond ONLY with valid JSON in this exact shape (no markdown, no prose):\n{\"category\": \"bug|feature|question|spam\", \"reason\": \"short explanation, max 200 chars\"}\n\nIssue Title: {{ $json.issue_title }}\nIssue Body:\n{{ $json.issue_body }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "retryOnFail": true,
      "typeVersion": 1.8,
      "waitBetweenTries": 2000
    },
    {
      "id": "bc77aba2-a530-4248-8f9a-a78dd0f8efbe",
      "name": "Route by Category",
      "type": "n8n-nodes-base.switch",
      "position": [
        2384,
        624
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "bug",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "s1",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.message.content.toLowerCase().trim() }}",
                    "rightValue": "bug"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "question",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "s2",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.message.content.toLowerCase().trim() }}",
                    "rightValue": "question"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "feature",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "s3",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.message.content.toLowerCase().trim() }}",
                    "rightValue": "feature"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "spam",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "s4",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.message.content.toLowerCase().trim() }}",
                    "rightValue": "spam"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "a1636638-8f03-49d8-9fe5-32bd54d3154e",
      "name": "Add Bug Label",
      "type": "n8n-nodes-base.github",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2640,
        288
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_owner }}"
        },
        "operation": "edit",
        "editFields": {
          "labels": [
            "bug",
            "triage:auto"
          ]
        },
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_repo }}"
        },
        "issueNumber": "={{ $('Extract Issue Info').item.json.issue_number }}",
        "authentication": "oAuth2"
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "ca1e9be9-fa40-4bdf-a379-47f379bc53f7",
      "name": "Alert Dev Team",
      "type": "n8n-nodes-base.slack",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2912,
        288
      ],
      "parameters": {
        "text": "=\ud83d\udc1b *New bug report triaged*\n\n\u2022 Title: {{ $('Calculate Similarity').item.json.issue_title }}\n\u2022 #{{ $('Calculate Similarity').item.json.issue_number }}: {{ $('Calculate Similarity').item.json.issue_html_url }}\n\u2022 Reporter: {{ $('Calculate Similarity').item.json.issue_author }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.dev_slack_channel }}"
        },
        "otherOptions": {}
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 2000
    },
    {
      "id": "9e8a9ea2-cd28-4bd0-bd4d-6974c560b151",
      "name": "Post FAQ Comment",
      "type": "n8n-nodes-base.github",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2640,
        560
      ],
      "parameters": {
        "body": "=\ud83d\udc4b Thanks for your question! Many common questions are answered in our FAQ: {{ $('Set Configuration').item.json.faq_url }}\n\nIf you can't find your answer there, please reply with more details and a maintainer will help you out.",
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_owner }}"
        },
        "operation": "createComment",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_repo }}"
        },
        "issueNumber": "={{ $('Extract Issue Info').item.json.issue_number }}",
        "authentication": "oAuth2"
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "42d56db6-1810-475e-9238-c3913162050b",
      "name": "Add to Roadmap",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2656,
        848
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $('Calculate Similarity').item.json.issue_html_url }}",
            "title": "={{ $('Calculate Similarity').item.json.issue_title }}",
            "author": "={{ $('Calculate Similarity').item.json.issue_author }}",
            "status": "proposed",
            "date_added": "={{ $now.toISO() }}",
            "issue_number": "={{ $('Calculate Similarity').item.json.issue_number }}"
          },
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.roadmap_sheet_name }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Configuration').item.json.roadmap_sheet_id }}"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.5,
      "waitBetweenTries": 2000
    },
    {
      "id": "412816df-f1e3-4004-bb01-1ae73a0e8e49",
      "name": "Close as Spam",
      "type": "n8n-nodes-base.github",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        2656,
        1136
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_owner }}"
        },
        "operation": "edit",
        "editFields": {
          "state": "closed",
          "labels": [
            "spam"
          ]
        },
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Configuration').item.json.github_repo }}"
        },
        "issueNumber": "={{ $('Extract Issue Info').item.json.issue_number }}",
        "authentication": "oAuth2"
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "st-ixgwv4tygb",
      "name": "README",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -608,
        192
      ],
      "parameters": {
        "width": 628,
        "height": 928,
        "content": "# Triage GitHub Issues with AI Category and Embedding-Based Duplicate Detection\n\nWhen a new Issue is opened, the workflow fetches recent Issues from the same repo, runs batch embeddings to calculate semantic similarity, and auto-closes duplicates with a reference comment. Non-duplicates are classified by AI into one of four categories and routed to the matching action branch.\n\n## How it works\n1. Receive a GitHub webhook when a new Issue is opened\n2. Filter for the \"opened\" action only\n3. Fetch the last 30 Issues from the repository\n4. Send all Issue texts to OpenAI embeddings in a single batch call\n5. Calculate cosine similarity between the new Issue and each past Issue\n6. If similarity exceeds 0.85, auto-close as duplicate with a reference comment\n7. Otherwise, classify the Issue with AI into bug, question, feature, or spam\n8. Route to the matching action branch\n\n## Categories\n- Bug: Add label and send Slack alert to dev team\n- Question: Post FAQ link as a comment\n- Feature: Append to roadmap Google Sheet\n- Spam: Auto-close with spam label\n- Duplicate: Auto-close with reference to the original Issue\n\n## Setup steps\n1. Generate a GitHub Personal Access Token with repo scope\n2. Create a webhook on your repository subscribing to Issues events\n3. Create a Google Sheet named feature_roadmap with columns: date_added, issue_number, title, author, url, status\n4. Open Set Configuration and fill in the repo owner, repo name, Sheet ID, Slack channel, and FAQ URL\n5. Connect GitHub, OpenAI, Google Sheets, and Slack credentials\n6. Activate the workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "st-fgn6gf7bzv",
      "name": "Section 1 Trigger & Filter",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 816,
        "height": 340,
        "content": "## 1. Trigger & Filter\n\nGitHub webhook receives Issue events \u2192 load central configuration (owner/repo, thresholds, FAQ URL, Slack channel, Roadmap sheet) \u2192 keep only `opened` actions and extract the issue number / title / body for downstream use."
      },
      "typeVersion": 1
    },
    {
      "id": "st-x39gpewg44",
      "name": "Section 2 Fetch & Embed",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 340,
        "content": "## 2. Fetch & Embed\n\nPull the last 30 Issues from the same repo, combine titles + bodies, and send them all to the OpenAI embeddings API in a single batch request for cost & latency efficiency."
      },
      "typeVersion": 1
    },
    {
      "id": "st-kuse1id055",
      "name": "Section 3 Similarity & Dedup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1584,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 340,
        "content": "## 3. Similarity & Dedup\n\nCompute cosine similarity between the new Issue and every fetched Issue. If the top score exceeds `duplicate_threshold` (default 0.85) \u2192 route to the Duplicate branch, otherwise continue to AI classification."
      },
      "typeVersion": 1
    },
    {
      "id": "st-rtusm0gvlj",
      "name": "Section 4 Classify & Dispatch",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2032,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 388,
        "content": "## 4. Classify & Dispatch\n\nGPT-4o-mini labels the Issue as one of bug / feature / question / spam (deterministic JSON). A Switch node fans out to five side effects: duplicate handling, bug alerting, FAQ reply, roadmap logging, and spam closure."
      },
      "typeVersion": 1
    },
    {
      "id": "st-wrb2p6qnld",
      "name": "Branch: Post Duplicate Comment",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2032,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 264,
        "content": "### \ud83d\udd01 Duplicate \u2192 Comment\nPolite \"possible duplicate\" comment posted on the new Issue with the similarity score."
      },
      "typeVersion": 1
    },
    {
      "id": "st-49ic6vbdyf",
      "name": "Branch: Close as Duplicate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2304,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 264,
        "content": "### \ud83d\udd12 Duplicate \u2192 Close\nCloses the Issue and applies the `duplicate` label."
      },
      "typeVersion": 1
    },
    {
      "id": "st-4cgzl7vacl",
      "name": "Branch: Add Bug Label",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 264,
        "content": "### \ud83d\udc1b Bug \u2192 Label\nAdds the `bug` + `triage:auto` labels to the Issue."
      },
      "typeVersion": 1
    },
    {
      "id": "st-7dos69hjg4",
      "name": "Branch: Alert Dev Team",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2848,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 264,
        "content": "### \ud83d\udce3 Bug \u2192 Slack Alert\nPings the configured dev Slack channel with the issue link."
      },
      "typeVersion": 1
    },
    {
      "id": "st-h9m8bl1sz6",
      "name": "Branch: Post FAQ Comment",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 248,
        "content": "### \u2753 Question \u2192 FAQ Reply\nAuto-replies with a link to the FAQ documentation."
      },
      "typeVersion": 1
    },
    {
      "id": "st-w619lvcarm",
      "name": "Branch: Add to Roadmap",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 264,
        "content": "### \u2728 Feature \u2192 Roadmap\nAppends the request to a Google Sheets roadmap backlog."
      },
      "typeVersion": 1
    },
    {
      "id": "st-ibvc4cu84t",
      "name": "Branch: Close as Spam",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        1024
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 280,
        "content": "### \ud83d\udeab Spam \u2192 Close\nCloses the Issue immediately and applies the `spam` label."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "31feeac4-3bcf-4643-8c92-eb50053621b6",
  "connections": {
    "If Duplicate": {
      "main": [
        [
          {
            "node": "Post Duplicate Comment",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Classify Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Bug Label": {
      "main": [
        [
          {
            "node": "Alert Dev Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Issue": {
      "main": [
        [
          {
            "node": "Route by Category",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Category": {
      "main": [
        [
          {
            "node": "Add Bug Label",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Post FAQ Comment",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Add to Roadmap",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Close as Spam",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Configuration": {
      "main": [
        [
          {
            "node": "Only On Issue Opened",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Issue Info": {
      "main": [
        [
          {
            "node": "Fetch Recent Issues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Recent Issues": {
      "main": [
        [
          {
            "node": "Prepare Texts for Embedding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Similarity": {
      "main": [
        [
          {
            "node": "If Duplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub Issue Webhook": {
      "main": [
        [
          {
            "node": "Set Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only On Issue Opened": {
      "main": [
        [
          {
            "node": "Extract Issue Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Embeddings (Batch)": {
      "main": [
        [
          {
            "node": "Calculate Similarity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Duplicate Comment": {
      "main": [
        [
          {
            "node": "Close as Duplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Texts for Embedding": {
      "main": [
        [
          {
            "node": "Get Embeddings (Batch)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}