{
  "id": "OaJZv61mZgAxF68I",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automatically publish LinkedIn posts using trends and AI quality checks",
  "tags": [
    {
      "id": "mHQntpbppFUpZPGm",
      "name": "template",
      "createdAt": "2026-02-17T08:30:26.885Z",
      "updatedAt": "2026-02-17T08:30:26.885Z"
    }
  ],
  "nodes": [
    {
      "id": "28905460-bb4c-4447-ba62-deb4d546861c",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1440,
        864
      ],
      "parameters": {
        "width": 700,
        "height": 1060,
        "content": "## Automatically publish LinkedIn posts using trends and AI quality checks\n\nAutomate your LinkedIn content pipeline: scan trending tech topics daily, generate drafts with AI, then let an AI selector and quality gate pick and publish the best one.\n\n## How it works\n\n**Flow 1 \u2014 Daily Research (6 AM):**\n1. Fetches trending topics from Hacker News, Reddit (8 subreddits), and Product Hunt in parallel\n2. Merges all sources, runs 7-layer deduplication, and ranks by relevance\n3. AI generates 3 LinkedIn post drafts with different angles (build story, insight, how-to)\n4. Saves all drafts to a Google Sheets queue with status tracking\n\n**Flow 2 \u2014 Smart Publish (Tue\u2013Thu 9:30 AM):**\n1. Reads all unpublished drafts from Google Sheets\n2. AI Selector picks the single best draft for today\n3. Selected post passes through an AI Quality Gate (scored 1\u201310)\n4. Approved \u2192 publish to LinkedIn + hashtag comment\n5. Needs revision \u2192 AI rewrites, then publishes\n6. Rejected \u2192 skipped, next best queued\n7. Telegram notification on every action\n\n## Setup steps\n\n1. **Google Sheets** \u2014 Create spreadsheet with columns: Post ID, Angle, Hook Line, Full Post, Hashtags, Trend Referenced, Word Count, Best Day, Posting Notes, Status, Created Date, Published Date, LinkedIn URL, AI Review, Revised Post, Dedup Stats, Generated At\n2. **Ollama** \u2014 Install locally and pull your model (e.g., `ollama pull mistral`)\n3. **LinkedIn OAuth** \u2014 Register app at developer.linkedin.com with `w_member_social` scope, get Person URN\n4. **Telegram Bot** \u2014 Create via @BotFather, get bot token and chat ID\n5. **AI Prompts** \u2014 Edit all 3 system prompts (Writer, Selector, Quality Gate) with your name, expertise, and tone\n6. **Test** \u2014 Run research flow manually first, then test publish flow\n\n## Customization\n\n- Tweak AI Selector weights in its system prompt\n- Adjust Quality Gate score thresholds as needed\n- Set any draft status to `force_publish` to override\n- Add more sources by duplicating the fetch/normalize pattern\n- Change schedule via cron expressions in Schedule Trigger nodes"
      },
      "typeVersion": 1
    },
    {
      "id": "453d29b4-33aa-47f0-bf85-7593e80f2ca9",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        720
      ],
      "parameters": {
        "color": 7,
        "width": 344,
        "height": 616,
        "content": "## Fetch trending topics\nHacker News, Reddit, and Product Hunt fetched in parallel."
      },
      "typeVersion": 1
    },
    {
      "id": "06951d31-821b-4bac-ad83-6b172cd7c3a5",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        720
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 600,
        "content": "## Normalize fetched data\nStandardize output format from each source."
      },
      "typeVersion": 1
    },
    {
      "id": "9d91bd5e-9b7c-4f87-b6f8-2969b7bfe292",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        496
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 420,
        "content": "## Merge, deduplicate, and generate drafts\nWait for all sources, run 7-layer dedup, rank, then AI writes 3 drafts."
      },
      "typeVersion": 1
    },
    {
      "id": "e98844f7-48e2-456e-a60a-fd0cb043b742",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2096,
        576
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 324,
        "content": "## Save drafts to Google Sheets\nAppend generated posts to the queue with status \"draft\"."
      },
      "typeVersion": 1
    },
    {
      "id": "9abded7f-8f92-40c8-b43a-f85801b49218",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        2240
      ],
      "parameters": {
        "color": 7,
        "width": 692,
        "height": 344,
        "content": "## Read drafts and select best post\nLoad all drafts, filter unpublished, AI Selector picks the best one for today."
      },
      "typeVersion": 1
    },
    {
      "id": "6310c9eb-b360-48fb-ba2e-40787cb06b3e",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        2080
      ],
      "parameters": {
        "color": 7,
        "width": 1004,
        "height": 636,
        "content": "## AI Quality Gate and review\nScore the selected post across 5 criteria, then route to approve, revise, or reject."
      },
      "typeVersion": 1
    },
    {
      "id": "b07ad6a2-3f61-44ef-be78-718ad08854c7",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1856,
        2144
      ],
      "parameters": {
        "color": 7,
        "width": 1452,
        "height": 636,
        "content": "## Publish to LinkedIn and notify\nPublish approved or revised post, add hashtag comment, update sheet, send Telegram notification."
      },
      "typeVersion": 1
    },
    {
      "id": "56d71465-5a9a-4ee5-b98c-675cccb9b783",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2320,
        1920
      ],
      "parameters": {
        "color": 2,
        "width": 484,
        "height": 152,
        "content": "## \u26a0\ufe0f Replace YOUR_LINKEDIN_PERSON_ID\nUpdate the Publish and Comment nodes with your LinkedIn Person URN."
      },
      "typeVersion": 1
    },
    {
      "id": "96b5a172-1663-4e41-9f96-e71b41b5f3cd",
      "name": "\u23f0 Daily 6 AM \u2014 Research",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -448,
        992
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 6 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c9e2a585-79cd-4b96-a924-bac5a004e99e",
      "name": "\ud83d\udfe0 Fetch Hacker News",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        -176,
        832
      ],
      "parameters": {
        "url": "https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=30",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d9abf24d-14a5-4956-8a8b-193ae12c5c8f",
      "name": "\ud83d\udd34 Fetch Reddit",
      "type": "n8n-nodes-base.code",
      "position": [
        -176,
        1008
      ],
      "parameters": {
        "jsCode": "const subreddits = ['technology','programming','startups','machinelearning','artificial','SaaS','webdev','entrepreneurship'];\nconst results = [];\nfor (const sub of subreddits) {\n  try {\n    const response = await this.helpers.httpRequest({ method:'GET', url:`https://www.reddit.com/r/${sub}/hot.json?limit=8`, headers:{'User-Agent':'n8n-linkedin-poster/1.0'}, timeout:10000, json:true });\n    if (response?.data?.children) {\n      for (const post of response.data.children) {\n        const d = post.data;\n        if (d?.title && !d.stickied) {\n          results.push({json:{title:d.title,url:d.url||`https://reddit.com${d.permalink}`,permalink:`https://reddit.com${d.permalink}`,score:d.score||0,comments:d.num_comments||0,subreddit:sub,selftext:(d.selftext||'').substring(0,300),source:'reddit',sourceLabel:`r/${sub}`,created:d.created_utc?new Date(d.created_utc*1000).toISOString():new Date().toISOString()}});\n        }\n      }\n    }\n  } catch(e) {}\n}\nreturn results.length>0?results:[{json:{source:'reddit',noData:true,items:0}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "1122d916-99b6-4b30-a24d-63517f1f6d99",
      "name": "\ud83d\udfe3 Fetch Product Hunt",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        -176,
        1184
      ],
      "parameters": {
        "url": "https://www.producthunt.com/feed",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0"
            },
            {
              "name": "Accept",
              "value": "application/rss+xml,text/xml,*/*"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "466b92a7-9003-4945-9244-08354bd22f66",
      "name": "\ud83d\udd27 Normalize HN",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        816
      ],
      "parameters": {
        "jsCode": "const items=$input.all();const n=[];\nfor(const item of items){const d=item.json;if(d.hits&&Array.isArray(d.hits)){for(const h of d.hits){if(h.title&&h.title.length>3){n.push({json:{title:h.title,url:h.url||`https://news.ycombinator.com/item?id=${h.objectID}`,discussionUrl:`https://news.ycombinator.com/item?id=${h.objectID}`,score:h.points||0,comments:h.num_comments||0,author:h.author||'',source:'hackernews',sourceLabel:'Hacker News',created:h.created_at||new Date().toISOString(),_normalized:true}});}}}else if(d.title){n.push({json:{title:d.title,url:d.url||'',score:d.points||d.score||0,comments:d.num_comments||0,source:'hackernews',sourceLabel:'Hacker News',created:d.created_at||new Date().toISOString(),_normalized:true}});}}\nreturn n.length>0?n:[{json:{source:'hackernews',noData:true,_normalized:true}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "3da3f175-2a43-4cc9-8c7c-405aca3fcef4",
      "name": "\ud83d\udd27 Normalize Reddit",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        1008
      ],
      "parameters": {
        "jsCode": "const items=$input.all();const n=[];\nfor(const item of items){const d=item.json;if(d.title&&!d.noData){n.push({json:{...d,_normalized:true}});}}\nreturn n.length>0?n:[{json:{source:'reddit',noData:true,_normalized:true}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c20ba66a-0cf5-4af0-86c5-f41d86527c09",
      "name": "\ud83d\udd27 Normalize PH",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        1200
      ],
      "parameters": {
        "jsCode": "const items=$input.all();const n=[];\nfor(const item of items){let raw=typeof item.json==='string'?item.json:(item.json.data||item.json.body||JSON.stringify(item.json));raw=String(raw);const ms=raw.match(/<item[\\s\\S]*?<\\/item>/gi)||[];\nfor(const xml of ms.slice(0,20)){const g=(tag)=>{const m=xml.match(new RegExp(`<${tag}[^>]*><!\\\\[CDATA\\\\[([^\\\\]]*?)\\\\]\\\\]><\\\\/${tag}>`,'i'))||xml.match(new RegExp(`<${tag}[^>]*>([^<]*)<\\\\/${tag}>`,'i'));return m?m[1].trim():'';};const t=g('title'),l=g('link')||g('guid');\nif(t&&l){n.push({json:{title:t,url:l,description:g('description').replace(/<[^>]+>/g,' ').substring(0,300),score:0,comments:0,source:'producthunt',sourceLabel:'Product Hunt',created:g('pubDate')||new Date().toISOString(),_normalized:true}});}}}\nreturn n.length>0?n:[{json:{source:'producthunt',noData:true,_normalized:true}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f4cb691e-9351-4965-8412-9932bf9d275c",
      "name": "\u23f3 Wait for ALL Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        432,
        960
      ],
      "parameters": {
        "numberInputs": 3
      },
      "typeVersion": 3.1
    },
    {
      "id": "913e3121-6adb-43b3-8e62-3a6e7160d7d4",
      "name": "\ud83d\udce6 Collect All Items",
      "type": "n8n-nodes-base.code",
      "position": [
        688,
        688
      ],
      "parameters": {
        "jsCode": "const all=$input.all();const c=[];\nfor(const item of all){const d=item.json;if(d.noData||!d.title||!d._normalized||d.title.length<5)continue;c.push({title:d.title||'',url:d.url||'',discussionUrl:d.discussionUrl||d.permalink||'',score:d.score||0,comments:d.comments||0,source:d.source||'unknown',sourceLabel:d.sourceLabel||d.source||'Unknown',created:d.created||new Date().toISOString(),description:d.description||d.selftext||'',author:d.author||'',subreddit:d.subreddit||''});}\nconst sc={};for(const i of c){sc[i.source]=(sc[i.source]||0)+1;}\nreturn{json:{allItems:c,totalCollected:c.length,sourceCounts:sc,collectedAt:new Date().toISOString()}};"
      },
      "typeVersion": 2
    },
    {
      "id": "a0e55c6e-91e8-4e6a-b874-e56fd0a4761e",
      "name": "\ud83e\uddf9 Deduplicate + Rank",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        672
      ],
      "parameters": {
        "jsCode": "const input=$json;const allItems=input.allItems||[];\nif(!allItems.length)return{json:{itemCount:0,totalScanned:0,topItems:[],topItemsFormatted:'No items.',stats:{totalFetched:0,duplicatesRemoved:0,afterDedup:0},scannedAt:new Date().toISOString()}};\nconst sw=new Set(['the','and','for','are','but','not','you','all','can','has','its','may','new','now','old','see','way','who','did','get','got','had','her','him','his','how','let','our','own','say','she','too','use','was','why','with','from','that','this','what','when','will','your','been','have','just','more','most','much','than','them','then','they','very','about','after','being','could','every','first','their','there','these','thing','those','using','which','would','should','into','some','also','like','over','such','take','only','come','make','know','year','back','even','give','many']);\nfunction nu(u){if(!u)return'';return u.toLowerCase().replace(/https?:\\/\\//,'').replace(/^www\\./,'').replace(/\\/+$/,'').replace(/\\?.*$/,'').replace(/#.*$/,'').replace(/\\/amp\\/?$/,'').trim();}\nfunction ec(u){if(!u)return'';const n=nu(u);if(n.includes('reddit.com/r/'))return'';if(n.includes('news.ycombinator.com'))return'';if(n.includes('producthunt.com/posts/')){const m=n.match(/producthunt\\.com\\/posts\\/([a-z0-9-]+)/);return m?m[1]:n;}return n;}\nfunction nt(t){return(t||'').toLowerCase().replace(/^(show hn|ask hn|tell hn|launch hn):\\s*/i,'').replace(/^\\[.*?\\]\\s*/,'').replace(/\\(.*?\\)/g,'').replace(/[^a-z0-9\\s]/g,' ').replace(/\\s+/g,' ').trim();}\nfunction gw(t){return nt(t).split(' ').filter(w=>w.length>2&&!sw.has(w));}\nfunction jc(a,b){const sA=new Set(a),sB=new Set(b);if(!sA.size||!sB.size)return 0;let i=0;for(const w of sA)if(sB.has(w))i++;return i/(sA.size+sB.size-i);}\nconst su=new Set(),sc=new Set(),ac=[],cr={};\nfor(const item of allItems){const nurl=nu(item.url),cu=ec(item.url),ntl=nt(item.title),ws=gw(item.title),p4=ws.slice(0,4).join(' '),p3=ws.slice(0,3).join(' ');\nlet dup=false,mi=-1;\nif(nurl&&su.has(nurl)){dup=true;mi=ac.findIndex(a=>nu(a.item.url)===nurl);}\nif(!dup&&cu&&cu.length>5&&sc.has(cu)){dup=true;mi=ac.findIndex(a=>ec(a.item.url)===cu);}\nif(!dup&&ntl.length>10){for(let i=0;i<ac.length;i++)if(ac[i].nt===ntl){dup=true;mi=i;break;}}\nif(!dup&&p4.length>12){for(let i=0;i<ac.length;i++)if(ac[i].p4.length>12&&ac[i].p4===p4){dup=true;mi=i;break;}}\nif(!dup&&p3.length>8){for(let i=0;i<ac.length;i++)if(ac[i].p3.length>8&&ac[i].p3===p3){dup=true;mi=i;break;}}\nif(!dup&&ws.length>=3){for(let i=0;i<ac.length;i++)if(ac[i].ws.length>=3&&jc(ws,ac[i].ws)>0.6){dup=true;mi=i;break;}}\nif(!dup&&ntl.length>15){for(let i=0;i<ac.length;i++)if(ac[i].nt.length>15&&(ntl.includes(ac[i].nt)||ac[i].nt.includes(ntl))){dup=true;mi=i;break;}}\nif(dup&&mi>=0){const tk=ac[mi].p4||ac[mi].nt;if(!cr[tk])cr[tk]={sources:new Set([ac[mi].item.source]),ms:ac[mi].item.score||0,tc:ac[mi].item.comments||0};cr[tk].sources.add(item.source);cr[tk].ms=Math.max(cr[tk].ms,item.score||0);cr[tk].tc+=(item.comments||0);continue;}\nif(nurl)su.add(nurl);if(cu&&cu.length>5)sc.add(cu);const tk=p4||ntl;cr[tk]={sources:new Set([item.source]),ms:item.score||0,tc:item.comments||0};ac.push({item,nt:ntl,ws,p4,p3});}\nconst en=ac.map(e=>{const tk=e.p4||e.nt,tr=cr[tk]||{sources:new Set([e.item.source]),ms:e.item.score,tc:e.item.comments};const s=tr.sources.size,sn=Math.min((tr.ms||0)/100,10),cn=Math.min((tr.tc||0)/50,10);const rel=Math.round(sn*3+cn*2+(s-1)*8+(e.item.created&&Date.now()-new Date(e.item.created).getTime()<12*3600000?3:0));return{...e.item,relevanceScore:rel,crossSourceCount:s,crossSources:Array.from(tr.sources).join(', '),isMultiSource:s>1,combinedScore:tr.ms,combinedComments:tr.tc};});\nen.sort((a,b)=>b.relevanceScore-a.relevanceScore);const top20=en.slice(0,20);const dr=allItems.length-ac.length;\nreturn{json:{stats:{totalFetched:allItems.length,duplicatesRemoved:dr,deduplicationRate:`${Math.round(dr/Math.max(allItems.length,1)*100)}%`,afterDedup:ac.length,topItemsCount:top20.length,multiSourceItems:en.filter(i=>i.isMultiSource).length,sourceCounts:input.sourceCounts},itemCount:top20.length,totalScanned:allItems.length,topItems:top20,topItemsFormatted:top20.map((item,i)=>{const m=item.isMultiSource?` \u2b50 [TRENDING on ${item.crossSources}]`:'';return`${i+1}. [${item.sourceLabel}] ${item.title}${m}\\n   Score: ${item.combinedScore} | Comments: ${item.combinedComments} | Relevance: ${item.relevanceScore}\\n   URL: ${item.url}`;}).join('\\n\\n'),scannedAt:new Date().toISOString()}};"
      },
      "typeVersion": 2
    },
    {
      "id": "f52fe204-cd16-4e46-a943-35c6e483ff57",
      "name": "\ud83d\udcca Has Items?",
      "type": "n8n-nodes-base.if",
      "position": [
        1104,
        672
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-items",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.itemCount }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c638109c-0e58-427f-aefc-06d92c2b1ebb",
      "name": "\ud83e\udd16 AI LinkedIn Writer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1440,
        528
      ],
      "parameters": {
        "text": "=Top {{ $json.itemCount }} trending topics:\n\n{{ $json.topItemsFormatted }}\n\nGenerate 3 LinkedIn post drafts. Each DIFFERENT angle.",
        "options": {
          "systemMessage": "You are a LinkedIn content strategist writing for: [YOUR NAME]\nExpertise: [YOUR EXPERTISE]\nProduct: [YOUR PRODUCT]\nTone: Smart, conversational, no corporate speak.\n\nGENERATE 3 POSTS:\n1. BUILD STORY \u2014 \"I built X that does Y.\"\n2. INSIGHT \u2014 \"Everyone talks about X. Here's what they miss.\"\n3. HOW-TO \u2014 \"How to do X in 5 steps.\"\n\nRULES: 150-250 words. Hook first line. Short paragraphs. Max 3 emoji. No hashtags in body. Soft CTA. Include specifics. Human tone. Reference real trends. \u2b50 items = prioritize.\n\nRAW JSON ONLY. No markdown fences.\n{\"posts\":[{\"angle\":\"build_story\",\"hookLine\":\"...\",\"fullPost\":\"...\",\"hashtags\":[\"#tag\"],\"trendReferenced\":\"...\",\"estimatedWordCount\":180,\"bestPostingDay\":\"Tuesday\",\"postingNotes\":\"...\"},{\"angle\":\"insight_opinion\",\"hookLine\":\"...\",\"fullPost\":\"...\",\"hashtags\":[],\"trendReferenced\":\"...\",\"estimatedWordCount\":200,\"bestPostingDay\":\"Wednesday\",\"postingNotes\":\"...\"},{\"angle\":\"howto_tutorial\",\"hookLine\":\"...\",\"fullPost\":\"...\",\"hashtags\":[],\"trendReferenced\":\"...\",\"estimatedWordCount\":220,\"bestPostingDay\":\"Thursday\",\"postingNotes\":\"...\"}],\"weekTheme\":\"...\",\"contentCalendarNote\":\"...\"}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "80f18a69-b090-437e-9343-7d7486fe9a85",
      "name": "\ud83d\udcdd Extract Posts",
      "type": "n8n-nodes-base.code",
      "position": [
        1936,
        720
      ],
      "parameters": {
        "jsCode": "const sourceData=$('\ud83d\udcca Has Items?').item.json;const raw=$json;let pd={};\nfunction ej(s){if(typeof s!=='string')return null;let c=s.replace(/```json\\s*/gi,'').replace(/```\\s*/g,'').trim();try{return JSON.parse(c);}catch(e){const m=c.match(/\\{[\\s\\S]*\"posts\"\\s*:[\\s\\S]*\\}/);if(m){try{return JSON.parse(m[0]);}catch(e2){return null;}}return null;}}\nfunction fa(o,d=0){if(d>5||!o||typeof o!=='object')return null;if(o.posts&&Array.isArray(o.posts))return o;const ps=['output','text','message','content','response','result','data','json','kwargs','lc_kwargs'];for(const p of ps){if(o[p]!==undefined){if(typeof o[p]==='string'){const r=ej(o[p]);if(r&&r.posts)return r;}else if(typeof o[p]==='object'){const f=fa(o[p],d+1);if(f)return f;}}}for(const k of Object.keys(o)){if(ps.includes(k))continue;const v=o[k];if(typeof v==='string'&&v.includes('\"posts\"')){const r=ej(v);if(r&&r.posts)return r;}else if(typeof v==='object'&&v!==null){const f=fa(v,d+1);if(f)return f;}}return null;}\ntry{if(raw&&raw.posts)pd=raw;else{const f=fa(raw);if(f)pd=f;}if(!pd.posts){const s=JSON.stringify(raw);const p=ej(s);if(p&&p.posts)pd=p;}}catch(e){}\nif(!pd.posts||!Array.isArray(pd.posts)||pd.posts.length===0){pd={posts:[{angle:'build_story',hookLine:'AI failed',fullPost:'Manual draft needed.',hashtags:['#n8n'],trendReferenced:'N/A',estimatedWordCount:0,bestPostingDay:'Tuesday',postingNotes:'AI failed'}],weekTheme:'N/A',contentCalendarNote:'Check AI'};}\nconst today=new Date().toISOString().split('T')[0];const results=[];\nfor(let i=0;i<pd.posts.length;i++){const p=pd.posts[i];results.push({json:{postId:`${today}-${i+1}-${p.angle}`,angle:p.angle||'unknown',hookLine:p.hookLine||'',fullPost:p.fullPost||'',hashtags:Array.isArray(p.hashtags)?p.hashtags.join(' '):(p.hashtags||''),trendReferenced:p.trendReferenced||'',wordCount:p.estimatedWordCount||0,bestPostingDay:p.bestPostingDay||'Tuesday',postingNotes:p.postingNotes||'',status:'draft',createdDate:today,weekTheme:pd.weekTheme||'',dedupStats:`${sourceData.stats.totalFetched}\u2192${sourceData.stats.duplicatesRemoved} dupes (${sourceData.stats.deduplicationRate})\u2192${sourceData.stats.afterDedup} unique`,generatedAt:new Date().toISOString()}});}\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "4137872b-da6d-43c9-a027-e2fec38881a5",
      "name": "\ud83d\udccb Save to Queue",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2208,
        720
      ],
      "parameters": {
        "columns": {
          "value": {
            "Angle": "={{ $json.angle }}",
            "Status": "={{ $json.status }}",
            "Post ID": "={{ $json.postId }}",
            "Best Day": "={{ $json.bestPostingDay }}",
            "Hashtags": "={{ $json.hashtags }}",
            "AI Review": "",
            "Full Post": "={{ $json.fullPost }}",
            "Hook Line": "={{ $json.hookLine }}",
            "Word Count": "={{ $json.wordCount }}",
            "Dedup Stats": "={{ $json.dedupStats }}",
            "Created Date": "={{ $json.createdDate }}",
            "Generated At": "={{ $json.generatedAt }}",
            "LinkedIn URL": "",
            "Revised Post": "",
            "Posting Notes": "={{ $json.postingNotes }}",
            "Published Date": "",
            "Trend Referenced": "={{ $json.trendReferenced }}"
          },
          "schema": [
            {
              "id": "Post ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Post ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Angle",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Angle",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hook Line",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Hook Line",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Full Post",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Full Post",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hashtags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Trend Referenced",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Trend Referenced",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Word Count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Word Count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Best Day",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Best Day",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Posting Notes",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Posting Notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Published Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Published Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "LinkedIn URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "LinkedIn URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AI Review",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "AI Review",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Revised Post",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Revised Post",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Dedup Stats",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Dedup Stats",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Generated At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Generated At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Post Queue"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "9b689f0a-f4d5-4ccd-9c69-88735a88501b",
      "name": "\ud83d\udca4 No Items",
      "type": "n8n-nodes-base.code",
      "position": [
        1472,
        832
      ],
      "parameters": {
        "jsCode": "return{json:{status:'no_trends',message:'No items after dedup.',checkedAt:new Date().toISOString()}};"
      },
      "typeVersion": 2
    },
    {
      "id": "2168cdbf-bf4c-4fc9-abe7-c3f1486b042f",
      "name": "\u23f0 Tue-Thu 9:30 AM \u2014 Publish",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -608,
        2368
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 9 * * 2,3,4"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "86b157cb-dcc5-447f-bc4a-301d673d47e6",
      "name": "\ud83d\udccb Read ALL Drafts",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -384,
        2368
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Post Queue"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "1fcae955-f777-407e-bc67-dcc4175b059b",
      "name": "\ud83d\udce6 Collect All Drafts",
      "type": "n8n-nodes-base.code",
      "position": [
        -160,
        2368
      ],
      "parameters": {
        "jsCode": "// ============================================\n// COLLECT ALL UNPUBLISHED DRAFTS FOR AI SELECTION\n// ============================================\n\nconst allRows = $input.all();\nconst drafts = [];\nconst recentlyPublished = [];\n\nconst dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\nconst today = new Date();\nconst todayName = dayNames[today.getDay()];\nconst todayDate = today.toISOString().split('T')[0];\n\nfor (const row of allRows) {\n  const r = row.json;\n  const status = (r.Status || r.status || '').toLowerCase().trim();\n  const published = r['Published Date'] || r.publishedDate || '';\n  \n  // Collect unpublished drafts\n  if ((status === 'draft' || status === 'pending_review' || status === 'approved') && !published) {\n    drafts.push({\n      postId: r['Post ID'] || r.postId || '',\n      angle: r.Angle || r.angle || '',\n      hookLine: r['Hook Line'] || r.hookLine || '',\n      fullPost: r['Full Post'] || r.fullPost || '',\n      hashtags: r.Hashtags || r.hashtags || '',\n      trendReferenced: r['Trend Referenced'] || r.trendReferenced || '',\n      wordCount: r['Word Count'] || r.wordCount || 0,\n      bestDay: r['Best Day'] || r.bestPostingDay || '',\n      postingNotes: r['Posting Notes'] || r.postingNotes || '',\n      createdDate: r['Created Date'] || r.createdDate || '',\n      status: status\n    });\n  }\n  \n  // Track recently published to avoid similar angles\n  if (status === 'published' || status === 'revised_and_published') {\n    recentlyPublished.push({\n      angle: r.Angle || r.angle || '',\n      publishedDate: published,\n      trendReferenced: r['Trend Referenced'] || ''\n    });\n  }\n}\n\nif (drafts.length === 0) {\n  return { json: {\n    hasDrafts: false,\n    message: 'No unpublished drafts in queue.',\n    checkedAt: new Date().toISOString()\n  }};\n}\n\n// Format drafts for AI selection\nconst draftsFormatted = drafts.map((d, i) => {\n  const dayMatch = d.bestDay === todayName ? ' \u2705 BEST DAY MATCH' : '';\n  return `--- DRAFT ${i + 1} ---\\nPost ID: ${d.postId}\\nAngle: ${d.angle}\\nHook: ${d.hookLine}\\nTrend: ${d.trendReferenced}\\nWord Count: ${d.wordCount}\\nBest Day: ${d.bestDay}${dayMatch}\\nCreated: ${d.createdDate}\\nStatus: ${d.status}\\n\\nFull Post:\\n${d.fullPost}\\n`;\n}).join('\\n');\n\n// Format recent posts for context\nconst recentContext = recentlyPublished.slice(-5).map(p => \n  `- ${p.angle} about \"${p.trendReferenced}\" on ${p.publishedDate}`\n).join('\\n') || 'No recent posts.';\n\nreturn { json: {\n  hasDrafts: true,\n  totalDrafts: drafts.length,\n  drafts: drafts,\n  draftsFormatted: draftsFormatted,\n  recentPostsContext: recentContext,\n  todayName: todayName,\n  todayDate: todayDate,\n  checkedAt: new Date().toISOString()\n}};"
      },
      "typeVersion": 2
    },
    {
      "id": "33472742-2b39-4a52-b0fd-835a09682c84",
      "name": "\ud83d\udcc4 Has Drafts?",
      "type": "n8n-nodes-base.if",
      "position": [
        64,
        2368
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-drafts",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.hasDrafts }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4869d6b6-cdc5-4915-9c53-6c9cba6898f0",
      "name": "\ud83e\udde0 AI Post Selector",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        544,
        2176
      ],
      "parameters": {
        "text": "=You have {{ $json.totalDrafts }} unpublished LinkedIn post drafts to choose from.\nToday is {{ $json.todayName }}, {{ $json.todayDate }}.\n\n--- AVAILABLE DRAFTS ---\n{{ $json.draftsFormatted }}\n\n--- RECENTLY PUBLISHED (avoid repeating) ---\n{{ $json.recentPostsContext }}\n\n--- SELECT THE BEST POST FOR TODAY ---",
        "options": {
          "systemMessage": "You are a LinkedIn content strategist. Your job is to analyze ALL available post drafts and select the SINGLE BEST one to publish today.\n\nYou are selecting for: [YOUR NAME]\nAudience: [YOUR AUDIENCE \u2014 e.g., founders, developers, tech leaders]\n\n---\n\nSELECTION CRITERIA (rank by importance):\n\n1. TODAY'S DAY ALIGNMENT (30% weight)\n   - If a post has bestDay matching today, it gets a strong boost\n   - Tuesday = build stories work best\n   - Wednesday = insights/opinions peak\n   - Thursday = how-to/tutorials perform well\n   - But don't blindly follow this \u2014 a great post beats a day-matched mediocre one\n\n2. HOOK STRENGTH (25% weight)\n   - Which draft has the most scroll-stopping first line?\n   - Specific > generic\n   - Curiosity/tension > announcement\n   - Does it make you want to read more?\n\n3. ANGLE VARIETY (20% weight)\n   - Look at recently published posts\n   - DO NOT pick the same angle as the last 2 posts\n   - Rotate: build_story \u2192 insight \u2192 howto \u2192 build_story\n   - If last post was insight, prefer build_story or howto\n\n4. TREND FRESHNESS (15% weight)\n   - Prefer posts about current/recent trends\n   - Older trends lose relevance fast\n   - Check the created date\n\n5. ENGAGEMENT POTENTIAL (10% weight)\n   - Will this generate comments?\n   - Is there a discussion hook?\n   - Is it polarizing enough to get reactions but not controversial?\n\n---\n\nDECISION OUTPUT:\n\nYou MUST select exactly ONE post. Provide the Post ID of your selection.\n\nIf ALL drafts are terrible, still pick the best one \u2014 the Quality Gate will catch truly bad posts.\n\nIf a post has status \"approved\" or \"force_publish\", prioritize it (human override).\n\n---\n\nRESPOND WITH ONLY RAW JSON. No markdown fences.\n\n{\n  \"selectedPostId\": \"the Post ID of the chosen draft\",\n  \"selectionReason\": \"2-3 sentences explaining why this post was chosen over others\",\n  \"dayAlignment\": \"matches\" or \"acceptable\" or \"mismatched\",\n  \"angleVariety\": \"good_rotation\" or \"acceptable\" or \"repetitive\",\n  \"hookRating\": 1 to 10,\n  \"trendFreshness\": \"fresh\" or \"recent\" or \"stale\",\n  \"engagementPrediction\": \"high\" or \"medium\" or \"low\",\n  \"overallConfidence\": 0.0 to 1.0,\n  \"alternatePostId\": \"backup Post ID in case primary is rejected by quality gate\",\n  \"alternateReason\": \"Why this is the backup choice\"\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "e9e97a87-8755-4bfb-819f-22fb0302c9b2",
      "name": "\ud83c\udfaf Extract Selection",
      "type": "n8n-nodes-base.code",
      "position": [
        896,
        2272
      ],
      "parameters": {
        "jsCode": "// Extract AI selection and prepare the chosen post\nconst draftsData = $('\ud83d\udcc4 Has Drafts?').item.json;\nconst rawAiOutput = $json;\n\nlet selectionData = {};\n\nfunction extractJSON(str) { if(typeof str!=='string')return null;let c=str.replace(/```json\\s*/gi,'').replace(/```\\s*/g,'').trim();try{return JSON.parse(c);}catch(e){const m=c.match(/\\{[\\s\\S]*\"selectedPostId\"\\s*:[\\s\\S]*\\}/);if(m){try{return JSON.parse(m[0]);}catch(e2){return null;}}return null;}}\nfunction findAI(obj,d=0){if(d>5||!obj||typeof obj!=='object')return null;if(obj.selectedPostId&&typeof obj.selectedPostId==='string')return obj;const ps=['output','text','message','content','response','result','data','json','kwargs','lc_kwargs'];for(const p of ps){if(obj[p]!==undefined){if(typeof obj[p]==='string'){const r=extractJSON(obj[p]);if(r&&r.selectedPostId)return r;}else if(typeof obj[p]==='object'){const f=findAI(obj[p],d+1);if(f)return f;}}}for(const k of Object.keys(obj)){if(ps.includes(k))continue;const v=obj[k];if(typeof v==='string'&&v.includes('\"selectedPostId\"')){const r=extractJSON(v);if(r&&r.selectedPostId)return r;}else if(typeof v==='object'&&v!==null){const f=findAI(v,d+1);if(f)return f;}}return null;}\n\ntry {\n  if (rawAiOutput && rawAiOutput.selectedPostId) selectionData = rawAiOutput;\n  else { const f = findAI(rawAiOutput); if (f) selectionData = f; }\n  if (!selectionData.selectedPostId) { const s = JSON.stringify(rawAiOutput); const p = extractJSON(s); if (p && p.selectedPostId) selectionData = p; }\n} catch (e) {}\n\n// Fallback: pick the first draft\nif (!selectionData.selectedPostId) {\n  selectionData = {\n    selectedPostId: draftsData.drafts[0].postId,\n    selectionReason: 'AI selector failed to parse \u2014 defaulting to first available draft.',\n    dayAlignment: 'unknown',\n    hookRating: 5,\n    overallConfidence: 0.3,\n    alternatePostId: draftsData.drafts.length > 1 ? draftsData.drafts[1].postId : '',\n    alternateReason: 'Backup in case primary fails.'\n  };\n}\n\n// Find the selected draft\nconst selectedDraft = draftsData.drafts.find(d => d.postId === selectionData.selectedPostId);\n\n// If selected post not found, use first draft\nconst finalDraft = selectedDraft || draftsData.drafts[0];\n\nreturn { json: {\n  // Selected post content\n  postId: finalDraft.postId,\n  fullPost: finalDraft.fullPost,\n  hookLine: finalDraft.hookLine,\n  hashtags: finalDraft.hashtags,\n  angle: finalDraft.angle,\n  trendReferenced: finalDraft.trendReferenced,\n  wordCount: finalDraft.wordCount,\n  postingNotes: finalDraft.postingNotes,\n  \n  // AI selection metadata\n  selectionReason: selectionData.selectionReason || '',\n  dayAlignment: selectionData.dayAlignment || '',\n  hookRating: selectionData.hookRating || 0,\n  engagementPrediction: selectionData.engagementPrediction || '',\n  overallConfidence: selectionData.overallConfidence || 0,\n  alternatePostId: selectionData.alternatePostId || '',\n  alternateReason: selectionData.alternateReason || '',\n  \n  // Context\n  totalDraftsConsidered: draftsData.totalDrafts,\n  todayName: draftsData.todayName,\n  selectedAt: new Date().toISOString()\n}};"
      },
      "typeVersion": 2
    },
    {
      "id": "c43512d3-fcc5-4cca-9ba7-38a8609b5dd7",
      "name": "\ud83d\udee1\ufe0f AI Quality Gate",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1120,
        2272
      ],
      "parameters": {
        "text": "=Review this LinkedIn post selected for publishing today.\n\n--- AI SELECTOR CONTEXT ---\nWhy selected: {{ $json.selectionReason }}\nDay alignment: {{ $json.dayAlignment }}\nHook rating: {{ $json.hookRating }}/10\nEngagement prediction: {{ $json.engagementPrediction }}\nConfidence: {{ $json.overallConfidence }}\n\n--- POST DETAILS ---\nAngle: {{ $json.angle }}\nHook: {{ $json.hookLine }}\nTrend: {{ $json.trendReferenced }}\nWord Count: {{ $json.wordCount }}\n\n--- FULL POST ---\n{{ $json.fullPost }}\n\n--- REVIEW NOW ---",
        "options": {
          "systemMessage": "You are a senior LinkedIn content quality reviewer for: [YOUR NAME]\nBrand voice: [YOUR TONE]\nAudience: [YOUR AUDIENCE]\n\nREVIEW CRITERIA (1-10 each):\n1. HOOK STRENGTH \u2014 Does it stop the scroll? Specific, not generic?\n2. VALUE DENSITY \u2014 Every sentence adds value? No fluff?\n3. AUTHENTICITY \u2014 Sounds human, not AI? Has specifics?\n4. ENGAGEMENT POTENTIAL \u2014 Will people comment/share?\n5. BRAND SAFETY \u2014 Nothing risky or off-brand?\n\nDECISION:\n- approve: Average \u2265 7, no score below 5\n- revise: Average 5-7 OR fixable issues. MUST provide complete revised post.\n- reject: Average < 5 OR brand safety < 5\n\nIF REVISE: Keep same angle/trend. Fix issues. Make MORE human. Improve hook if < 7. Return COMPLETE revised post.\n\nRAW JSON ONLY. No markdown fences.\n{\"decision\":\"approve\",\"scores\":{\"hookStrength\":8,\"valueDensity\":7,\"authenticity\":9,\"engagementPotential\":7,\"brandSafety\":10},\"averageScore\":8.2,\"reviewSummary\":\"2-3 sentences\",\"issuesFound\":[],\"revisedPost\":\"\",\"revisedHookLine\":\"\",\"confidenceLevel\":\"high\",\"publishRecommendation\":\"One sentence\"}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "3f282eb2-d920-4c0b-888c-3a30625450aa",
      "name": "\ud83c\udfaf Extract Review",
      "type": "n8n-nodes-base.code",
      "position": [
        1920,
        2272
      ],
      "parameters": {
        "jsCode": "const postData = $('\ud83c\udfaf Extract Selection').item.json;\nconst raw = $json;\nlet rv = {};\n\nfunction ej(s){if(typeof s!=='string')return null;let c=s.replace(/```json\\s*/gi,'').replace(/```\\s*/g,'').trim();try{return JSON.parse(c);}catch(e){const m=c.match(/\\{[\\s\\S]*\"decision\"\\s*:[\\s\\S]*\\}/);if(m){try{return JSON.parse(m[0]);}catch(e2){return null;}}return null;}}\nfunction fa(o,d=0){if(d>5||!o||typeof o!=='object')return null;if(o.decision&&typeof o.decision==='string')return o;const ps=['output','text','message','content','response','result','data','json','kwargs','lc_kwargs'];for(const p of ps){if(o[p]!==undefined){if(typeof o[p]==='string'){const r=ej(o[p]);if(r&&r.decision)return r;}else if(typeof o[p]==='object'){const f=fa(o[p],d+1);if(f)return f;}}}for(const k of Object.keys(o)){if(ps.includes(k))continue;const v=o[k];if(typeof v==='string'&&v.includes('\"decision\"')){const r=ej(v);if(r&&r.decision)return r;}else if(typeof v==='object'&&v!==null){const f=fa(v,d+1);if(f)return f;}}return null;}\ntry{if(raw&&raw.decision)rv=raw;else{const f=fa(raw);if(f)rv=f;}if(!rv.decision){const s=JSON.stringify(raw);const p=ej(s);if(p&&p.decision)rv=p;}}catch(e){}\n\nif(!rv.decision||!['approve','revise','reject'].includes(rv.decision)){\n  rv={decision:'approve',scores:{hookStrength:7,valueDensity:7,authenticity:7,engagementPotential:7,brandSafety:8},averageScore:7.2,reviewSummary:'AI review parse failed \u2014 defaulting to approve.',issuesFound:['parse failed'],revisedPost:'',revisedHookLine:'',confidenceLevel:'low',publishRecommendation:'Review manually.'};\n}\n\nlet finalPost=postData.fullPost;\nlet finalHook=postData.hookLine;\nlet wasRevised=false;\n\nif(rv.decision==='revise'&&rv.revisedPost){\n  finalPost=rv.revisedPost;\n  finalHook=rv.revisedHookLine||rv.revisedPost.split('\\n')[0];\n  wasRevised=true;\n}\n\nreturn{json:{\n  postId:postData.postId,angle:postData.angle,hashtags:postData.hashtags,trendReferenced:postData.trendReferenced,\n  fullPost:finalPost,hookLine:finalHook,wasRevised:wasRevised,\n  decision:rv.decision,scores:rv.scores||{},averageScore:rv.averageScore||0,\n  reviewSummary:rv.reviewSummary||'',issuesFound:Array.isArray(rv.issuesFound)?rv.issuesFound.join('; '):'',\n  confidenceLevel:rv.confidenceLevel||'medium',publishRecommendation:rv.publishRecommendation||'',\n  selectionReason:postData.selectionReason,totalDraftsConsidered:postData.totalDraftsConsidered,\n  alternatePostId:postData.alternatePostId,\n  reviewedAt:new Date().toISOString()\n}};"
      },
      "typeVersion": 2
    },
    {
      "id": "847a584d-db26-49f3-96c2-843182055170",
      "name": "\ud83d\udd00 Review Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        2080,
        2272
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.decision }}",
                    "rightValue": "approve"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.decision }}",
                    "rightValue": "revise"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.decision }}",
                    "rightValue": "reject"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "allMatchingOutputs": false
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "7904330d-bbeb-46a8-9b47-df4daaacd0b7",
      "name": "\ud83d\udce4 Publish to LinkedIn",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        2528,
        2256
      ],
      "parameters": {
        "url": "https://api.linkedin.com/v2/ugcPosts",
        "method": "POST",
        "options": {
          "timeout": 30000
        },
        "jsonBody": "={\n  \"author\": \"urn:li:person:YOUR_LINKEDIN_PERSON_ID\",\n  \"lifecycleState\": \"PUBLISHED\",\n  \"specificContent\": {\n    \"com.linkedin.ugc.ShareContent\": {\n      \"shareCommentary\": {\n        \"text\": {{ JSON.stringify($json.fullPost) }}\n      },\n      \"shareMediaCategory\": \"NONE\"\n    }\n  },\n  \"visibility\": {\n    \"com.linkedin.ugc.MemberNetworkVisibility\": \"PUBLIC\"\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Restli-Protocol-Version",
              "value": "2.0.0"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2b8ea7f7-8be2-4147-924b-ab200d877d5c",
      "name": "\ud83d\udcac Hashtags Comment",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        2752,
        2256
      ],
      "parameters": {
        "url": "=https://api.linkedin.com/v2/socialActions/{{ encodeURIComponent($json.id) }}/comments",
        "method": "POST",
        "options": {
          "timeout": 15000
        },
        "jsonBody": "={\n  \"actor\": \"urn:li:person:YOUR_LINKEDIN_PERSON_ID\",\n  \"message\": {\n    \"text\": {{ JSON.stringify($('\ud83c\udfaf Extract Review').item.json.hashtags) }}\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e58d4a3e-13a9-4711-bbcf-6c59deba9aa6",
      "name": "\u2705 Mark Published",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2976,
        2368
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "={{ $('\ud83c\udfaf Extract Review').item.json.wasRevised ? 'revised_and_published' : 'published' }}",
            "Post ID": "={{ $('\ud83c\udfaf Extract Review').item.json.postId }}",
            "AI Review": "=Selected: {{ $('\ud83c\udfaf Extract Review').item.json.selectionReason }} | Review: {{ $('\ud83c\udfaf Extract Review').item.json.reviewSummary }} | Score: {{ $('\ud83c\udfaf Extract Review').item.json.averageScore }}",
            "LinkedIn URL": "={{ $('\ud83d\udce4 Publish to LinkedIn').item.json.id || 'published' }}",
            "Revised Post": "={{ $('\ud83c\udfaf Extract Review').item.json.wasRevised ? $('\ud83c\udfaf Extract Review').item.json.fullPost : '' }}",
            "Published Date": "={{ new Date().toISOString() }}"
          },
          "schema": [
            {
              "id": "Post ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Post ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Published Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Published Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "LinkedIn URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "LinkedIn URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AI Review",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "AI Review",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Revised Post",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Revised Post",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Post ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Post Queue"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "84d1f3f2-5b8c-4508-81d4-9d8b4c1966ad",
      "name": "\ud83d\udcf2 Published \u2705",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2976,
        2176
      ],
      "parameters": {
        "text": "={{ (() => {\nconst r=$('\ud83c\udfaf Extract Review').item.json;\nconst rev=r.wasRevised?'\\n\u270f\ufe0f *Auto-revised by AI*':'';\nconst sel=r.selectionReason?`\\n\ud83e\udde0 *Why selected:* ${r.selectionReason}`:'';\nreturn `\u2705 *LinkedIn Post Published!*\\n\\n\ud83d\udcdd *Angle:* ${r.angle}\\n\ud83c\udfa3 *Hook:* ${r.hookLine}\\n\ud83d\udcca *Trend:* ${r.trendReferenced}\\n\\n\ud83d\udee1\ufe0f *Quality Score:* ${r.averageScore}/10\\n\ud83d\udcac ${r.reviewSummary}${rev}${sel}\\n\\n\ud83d\udccb *Selected from ${r.totalDraftsConsidered} drafts*\\n\u23f0 ${new Date().toISOString()}`;\n})() }}",
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b82b0d8e-4f37-4287-9179-2dc988442031",
      "name": "\ud83d\udcbe Save Revised",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2304,
        2352
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "revised_and_published",
            "Post ID": "={{ $json.postId }}",
            "AI Review": "=REVISED | {{ $json.reviewSummary }} | Score: {{ $json.averageScore }} | Issues: {{ $json.issuesFound }}",
            "Full Post": "={{ $json.fullPost }}",
            "Hook Line": "={{ $json.hookLine }}"
          },
          "schema": [
            {
              "id": "Post ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Post ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Full Post",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Full Post",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hook Line",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Hook Line",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AI Review",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "AI Review",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Post ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Post Queue"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "51003564-ba71-482d-8d7a-b8001acf4e13",
      "name": "\u274c Mark Rejected",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2304,
        2544
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "rejected",
            "Post ID": "={{ $json.postId }}",
            "AI Review": "=REJECTED | {{ $json.reviewSummary }} | Score: {{ $json.averageScore }} | Issues: {{ $json.issuesFound }}"
          },
          "schema": [
            {
              "id": "Post ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Post ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AI Review",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "AI Review",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Post ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Post Queue"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "1cb07995-533d-4bcc-a0aa-03937a8e0317",
      "name": "\ud83d\udcf2 Rejected \u274c",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2528,
        2544
      ],
      "parameters": {
        "text": "=\u274c *Post Rejected by AI Quality Gate*\n\n\ud83d\udcdd *Post ID:* {{ $json.postId }}\n\ud83d\udcca *Score:* {{ $json.averageScore }}/10\n\ud83d\udcac {{ $json.reviewSummary }}\n\u26a0\ufe0f *Issues:* {{ $json.issuesFound }}\n\ud83d\udd04 *Backup post:* {{ $json.alternatePostId || 'None' }}\n\n_Rejected posts are skipped. Next run will try a different post._",
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4a5a2e9f-88b8-4866-abe5-df05c0aa7e2b",
      "name": "\ud83d\udca4 No Drafts",
      "type": "n8n-nodes-base.code",
      "position": [
        608,
        2576
      ],
      "parameters": {
        "jsCode": "return{json:{status:'no_drafts',message:'No unpublished drafts. Run the research flow to generate new posts.',checkedAt:new Date().toISOString()}};"
      },
      "typeVersion": 2
    },
    {
      "id": "b490df19-a139-49d3-babf-af1ea76ce356",
      "name": "Ollama Writer",
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "position": [
        1376,
        672
      ],
      "parameters": {
        "model": "mistral",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "daee9f27-0977-4fc2-a54e-bf620e9a5baf",
      "name": "Ollama Selector",
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "position": [
        544,
        2400
      ],
      "parameters": {
        "model": "YOUR_MODEL_NAME",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "855f88fb-9cd0-434f-b9b9-dd9dd66e21db",
      "name": "Ollama Reviewer",
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "position": [
        1200,
        2496
      ],
      "parameters": {
        "model": "YOUR_MODEL_NAME",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "52662271-9a72-4ca1-a89d-e0c2fd39f027",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        1792
      ],
      "parameters": {
        "color": 2,
        "width": 404,
        "height": 216,
        "content": "##  **\u26a0\ufe0f Edit all 3 AI system prompts**\n**Update Writer, Selector, and Quality Gate with your name, expertise, and tone.**"
      },
      "typeVersion": 1
    },
    {
      "id": "0fd1bd37-522d-46d0-b0fc-da7198b6f22c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        512
      ],
      "parameters": {
        "width": 400,
        "height": 80,
        "content": "## **Flow 1 \u2014 Daily Research (6 AM):**"
      },
      "typeVersion": 1
    },
    {
      "id": "843d3376-3979-4c95-b4a8-1dc0d737a235",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        2096
      ],
      "parameters": {
        "width": 400,
        "height": 80,
        "content": "## **Flow 2 \u2014 Smart Publish (Tue\u2013Thu 9:30 AM):**"
      },
      "typeVersion": 1
    },
    {
      "id": "dcf66211-40c8-4075-bd32-4039780723b3",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        1792
      ],
      "parameters": {
        "color": 2,
        "width": 404,
        "height": 216,
        "content": "##  **\u26a0\ufe0f Edit all 3 AI system prompts**\n**Update Writer, Selector, and Quality Gate with your name, expertise, and tone.**"
      },
      "typeVersion": 1
    },
    {
      "id": "94ed6818-7093-4d7d-9ded-ddb127cf3476",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1408,
        1040
      ],
      "parameters": {
        "color": 2,
        "width": 404,
        "height": 216,
        "content": "##  **\u26a0\ufe0f Edit all 3 AI system prompts**\n**Update Writer, Selector, and Quality Gate with your name, expertise, and tone.**"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "c7512fc2-2c1c-4b0e-8007-d050d456d091",
  "connections": {
    "Ollama Writer": {
      "ai_languageModel": [
        [
          {
            "node": "\ud83e\udd16 AI LinkedIn Writer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Reviewer": {
      "ai_languageModel": [
        [
          {
            "node": "\ud83d\udee1\ufe0f AI Quality Gate",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Selector": {
      "ai_languageModel": [
        [
          {
            "node": "\ud83e\udde0 AI Post Selector",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca Has Items?": {
      "main": [
        [
          {
            "node": "\ud83e\udd16 AI LinkedIn Writer",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udca4 No Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcc4 Has Drafts?": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 AI Post Selector",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udca4 No Drafts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u274c Mark Rejected": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Rejected \u274c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcbe Save Revised": {
      "main": [
        [
          {
            "node": "\ud83d\udce4 Publish to LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd27 Normalize HN": {
      "main": [
        [
          {
            "node": "\u23f3 Wait for ALL Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd27 Normalize PH": {
      "main": [
        [
          {
            "node": "\u23f3 Wait for ALL Sources",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "\ud83d\udd34 Fetch Reddit": {
      "main": [
        [
          {
            "node": "\ud83d\udd27 Normalize Reddit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcdd Extract Posts": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Save to Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfaf Extract Review": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Review Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Read ALL Drafts": {
      "main": [
        [
          {
            "node": "\ud83d\udce6 Collect All Drafts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Review Decision": {
      "main": [
        [
          {
            "node": "\ud83d\udce4 Publish to LinkedIn",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udcbe Save Revised",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u274c Mark Rejected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcac Hashtags Comment": {
      "main": [
        [
          {
            "node": "\u2705 Mark Published",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udcf2 Published \u2705",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd27 Normalize Reddit": {
      "main": [
        [
          {
            "node": "\u23f3 Wait for ALL Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "\ud83e\udde0 AI Post Selector": {
      "main": [
        [
          {
            "node": "\ud83c\udfaf Extract Selection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfaf Extract Selection": {
      "main": [
        [
          {
            "node": "\ud83d\udee1\ufe0f AI Quality Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce6 Collect All Items": {
      "main": [
        [
          {
            "node": "\ud83e\uddf9 Deduplicate + Rank",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udfe0 Fetch Hacker News": {
      "main": [
        [
          {
            "node": "\ud83d\udd27 Normalize HN",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce6 Collect All Drafts": {
      "main": [
        [
          {
            "node": "\ud83d\udcc4 Has Drafts?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udee1\ufe0f AI Quality Gate": {
      "main": [
        [
          {
            "node": "\ud83c\udfaf Extract Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udfe3 Fetch Product Hunt": {
      "main": [
        [
          {
            "node": "\ud83d\udd27 Normalize PH",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udd16 AI LinkedIn Writer": {
      "main": [
        [
          {
            "node": "\ud83d\udcdd Extract Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\uddf9 Deduplicate + Rank": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Has Items?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f3 Wait for ALL Sources": {
      "main": [
        [
          {
            "node": "\ud83d\udce6 Collect All Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce4 Publish to LinkedIn": {
      "main": [
        [
          {
            "node": "\ud83d\udcac Hashtags Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Daily 6 AM \u2014 Research": {
      "main": [
        [
          {
            "node": "\ud83d\udfe0 Fetch Hacker News",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udd34 Fetch Reddit",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udfe3 Fetch Product Hunt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Tue-Thu 9:30 AM \u2014 Publish": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Read ALL Drafts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}