{
  "name": "13 - VK Public Discussion / Lead Source Connector (Stage 3.5)",
  "nodes": [
    {
      "parameters": {
        "content": "## WF13 \u2014 VK Public Discussion / Lead Source Connector \u00b7 v0.3 (Stage 3.5, DEC-121/125/138)\n\n**\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435:** \u041f\u0415\u0420\u0412\u042b\u0419 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a Lead Scout \u2014 \u041f\u0423\u0411\u041b\u0418\u0427\u041d\u042b\u0415 \u043e\u0431\u0441\u0443\u0436\u0434\u0435\u043d\u0438\u044f VK (\u043f\u043e\u0441\u0442\u044b \u0438 \u041a\u041e\u041c\u041c\u0415\u041d\u0422\u0410\u0420\u0418\u0418 \u0433\u0440\u0443\u043f\u043f \u0438\u0437 allowlist). \u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u2014 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u0421\u041f\u0420\u041e\u0421/\u0431\u043e\u043b\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0435\u0439 (\u043d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u0430, \u043f\u043b\u043e\u0445\u0430\u044f \u041a\u0418, \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u041f\u0422\u0421, \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430) \u2192 question_objection/public_comment \u2192 raw_market_records \u2192 WF14 Lead Scout. \u041f\u043b\u044e\u0441 \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u044b \u0430\u0432\u0442\u043e\u0440\u043e\u0432 \u0434\u043b\u044f WF10.\n\n**\u0416\u0451\u0441\u0442\u043a\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430:** fixture-first ($0, \u0411\u0415\u0417 HTTP-\u0432\u044b\u0437\u043e\u0432\u043e\u0432); live_mode=false (guard \u0431\u0440\u043e\u0441\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443); \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u044b/\u043f\u043e\u0441\u0442\u044b/\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u2014 \u041d\u0418\u041a\u0410\u041a\u0418\u0425 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445 \u0447\u0430\u0442\u043e\u0432/\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432, \u0441\u043a\u0440\u044b\u0442\u044b\u0445 \u043a\u043e\u043d\u0442\u0430\u043a\u0442\u043e\u0432, \u0430\u0432\u0442\u043e-\u0430\u0443\u0442\u0440\u0438\u0447\u0430 (CONTACT_AND_OUTREACH_POLICY). \u041a\u043e\u043d\u0442\u0430\u043a\u0442 \u043f\u0438\u0448\u0435\u0442\u0441\u044f \u0422\u041e\u041b\u042c\u041a\u041e \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u043b\u043e\u0432\u043d\u043e \u0432\u0438\u0434\u0435\u043d \u0432 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u043c \u0442\u0435\u043a\u0441\u0442\u0435; profile_url \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0444\u0438\u043b\u044c \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439. \u0411\u0435\u0437 Claude. \u041a\u0430\u043d\u043e\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0445\u044d\u043d\u0434\u043e\u0444\u0444: raw_market_records \u2192 WF14 (Lead Scout) \u2192 public_lead_signals (\u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u0431\u0435\u0437 \u0430\u0432\u0442\u043e-handoff); WF08 \u2014 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0443\u0442\u044c Stage 3, \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0445\u044d\u043d\u0434\u043e\u0444\u0444. \u041f\u0438\u0448\u0435\u0442: agent_requests / raw_market_records (40) / market_record_registry (15) / live_source_runs (23). MSK +03:00.\n\n**v0.3 LIVE (gated, INERT):** \u0434\u0432\u0430 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e VK API \u2014 wall.get (\u0441\u0442\u0435\u043d\u044b \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f) \u0438 wall.getComments (\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043f\u043e\u0434 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u043f\u043e\u0441\u0442\u0430\u043c\u0438 \u2014 \u0433\u0434\u0435 \u0436\u0438\u0432\u0451\u0442 \u0441\u043f\u0440\u043e\u0441). \u0413\u0435\u0439\u0442: token=I_APPROVE_LIVE_VK_PUBLIC_DISCUSSION + live_group_allowlist \u0438/\u0438\u043b\u0438 live_post_allowlist \u2192 \u041e\u0422\u041a\u041b\u042e\u0427\u0401\u041d\u041d\u042b\u0419 HTTP (\u0442\u043e\u043a\u0435\u043d \u0422\u041e\u041b\u042c\u041a\u041e \u043a\u0430\u043a n8n-\u043a\u0440\u0435\u0434\u0435\u043d\u0448\u043b) \u2192 \u0438\u043d\u0435\u0440\u0442\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440 (\u0431\u0440\u043e\u0441\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0431\u0435\u0437 \u043e\u0442\u0432\u0435\u0442\u0430 API; \u043f\u0430\u0440\u0441\u0438\u0442 \u043e\u0431\u0435 \u0444\u043e\u0440\u043c\u044b). Runtime-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 live = PENDING_STAGE_C_ACCEPTANCE. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432\u0441\u0451 \u0438\u043d\u0435\u0440\u0442\u043d\u043e.",
        "height": 760,
        "width": 620
      },
      "id": "wf13-note-1",
      "name": "Overview Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -460,
        -340
      ]
    },
    {
      "parameters": {
        "content": "## Setup & Test (fixture \u2014 Stage 3.5)\n\n1. \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c SPREADSHEET_ID \u0432 Google Sheets \u043d\u043e\u0434\u044b (Read registry + Append raw/registry/agent_requests/live_source_runs).\n2. \u0417\u0430\u043f\u0443\u0441\u043a \u0432\u0440\u0443\u0447\u043d\u0443\u044e (active=false). \u041d\u0415 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u043d\u043e\u0434\u0435 \u2014 \u043f\u043e\u043b\u043d\u0430\u044f \u043f\u0440\u0438\u0451\u043c\u043a\u0430 = \u043f\u0430\u043a\u0435\u0442 Stage C.\n3. Fixture-\u043f\u0440\u043e\u0433\u043e\u043d (\u0441\u0438\u043d\u0442\u0435\u0442\u0438\u043a\u0430, $0): items_received=9, hard_skipped=1 (\u044e\u0440.\u0430\u0434\u0440\u0435\u0441), business_relevant=8, unique=7, duplicates=1 (in-batch \u0434\u0443\u0431\u043b\u044c comment 2011); raw +8 (audit), registry +7. \u0421\u0440\u0435\u0434\u0438 unique: 5 consumer-demand \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432 (question_objection/public_comment) \u2014 \u044d\u0442\u043e \u0432\u0445\u043e\u0434 Lead Scout; 1 competitor post; 1 market-news post. \u0422\u0435\u043b\u0435\u0444\u043e\u043d\u044b fixture = +7 000 ... (\u0441\u0438\u043d\u0442\u0435\u0442\u0438\u043a\u0430, \u043d\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435).\n4. \u041f\u043e\u0432\u0442\u043e\u0440: unique=0, duplicates=8, registry +0.\n5. Guard-\u0442\u0435\u0441\u0442: live_mode=true (fixture_mode=false) \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0430 \u2192 \u043e\u0448\u0438\u0431\u043a\u0430 \u043d\u0430 LIVE VK Approval Gate. \u0412\u0435\u0440\u043d\u0443\u0442\u044c fixture_mode=true.\n6. End-to-end Lead Scout (Stage C): WF13 fixture \u2192 WF14 Execute once \u2192 public_lead_signals ~5 \u0441\u0442\u0440\u043e\u043a \u0441\u043e scored lead_score/score_band; \u0441\u0438\u043d\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0442\u0435\u043b\u0435\u0444\u043e\u043d/username \u2192 public_phone/public_username (manual_review); \u0440\u0435\u043a\u043b\u0430\u043c\u0430/\u043d\u043e\u0432\u043e\u0441\u0442\u0438 \u041d\u0415 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043b\u0438\u0434\u0430\u043c\u0438; outreach_allowed=false.",
        "height": 640,
        "width": 620
      },
      "id": "wf13-note-2",
      "name": "Setup and Test Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        220,
        -340
      ]
    },
    {
      "parameters": {},
      "id": "wf13-start",
      "name": "Manual Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -420,
        560
      ]
    },
    {
      "parameters": {
        "jsCode": "// WF13 Public Discussion Connector \u2014 VK PUBLIC groups/posts/comments (DEC-121/125; Stage 3.5 lead-source, DEC-138).\n// Source decision (Avito -> Telegram -> VK): VK public discussions are the richest PUBLIC CONSUMER DEMAND source\n// (questions/objections/buying intent) and the first practical Lead Scout source. Fixture-first, $0, NO external\n// calls, no Claude, no auto-handoff. PUBLIC groups/posts/comments ONLY: no private chats/messages, no member\n// extraction, no hidden contacts, no auto-outreach (CONTACT_AND_OUTREACH_POLICY).\nfunction moscowIsoNow(){var m=new Date(Date.now()+10800000);return m.toISOString().replace('Z','+03:00');}\nfunction moscowStamp(){var z=function(n){return String(n).padStart(2,'0');};var m=new Date(Date.now()+10800000);return m.getUTCFullYear()+z(m.getUTCMonth()+1)+z(m.getUTCDate())+'_'+z(m.getUTCHours())+z(m.getUTCMinutes())+z(m.getUTCSeconds());}\nconst now=moscowIsoNow();\nconst stamp=moscowStamp();\nreturn [{ json: {\n  fixture_mode:true,\n  live_mode:false,\n  // --- live v0.3 GUARDED PATH (DEC-125/138): VK official API. INERT by default \u2014 live_mode=false, token empty,\n  // allowlists empty, HTTP node DISABLED. Two intended public methods:\n  //   wall.get        \u2014 public group walls (group/screen-name allowlist)\n  //   wall.getComments \u2014 PUBLIC POST comments (post-URL allowlist) \u2014 where consumer demand actually lives\n  // Enabling live requires ALL of: explicit operator approval (token), a non-empty allowlist for the chosen\n  // method(s), a VK API credential bound in n8n (token NEVER in this file), and enabling the disabled HTTP node.\n  // Hard scope: PUBLIC groups/posts/comments ONLY \u2014 no private messages, no closed groups, no member extraction,\n  // no hidden contacts, no auto-outreach, no mass people lists (aggregate author counts only).\n  live_transport:'vk_api',\n  live_methods:['wall.get','wall.getComments'],\n  live_approval_token:'',\n  live_group_allowlist:[],          // public group screen names / URLs (for wall.get)\n  live_post_allowlist:[],           // public post URLs e.g. https://vk.com/wall-111222_301 (for wall.getComments)\n  live_max_items_per_group:10,\n  live_requires_operator_approval:true,\n  platform:'vk',\n  source_type_post:'social_post',\n  source_type_comment:'social_comment',\n  requested_by:'operator',\n  request_type:'social_source_discovery',\n  region:'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e',\n  service_focus:'credit_broker',\n  group_allowlist:[\n    'kredity_msk_fixture',\n    'biznes_finansy_fixture',\n    'uslugi_msk_fixture'\n  ],\n  pipeline_limit:12,\n  // --- MONITORED VK GROUPS (Stage C.1, DEC-141): group -> recent posts -> RELEVANT posts -> PUBLIC comments.\n  // STAGED/INERT: monitored_mode=false -> live two-stage HTTP placeholders stay DISABLED (no network).\n  // monitored_fixture_mode=true runs a deterministic $0 SIMULATION (no VK call). PUBLIC data only: no private\n  // messages, no closed groups, no member/follower extraction, no hidden/inferred contacts, no auto-outreach.\n  // Real production groups are NEVER committed here \u2014 operator fills monitored_groups per the VK runbook.\n  monitored_mode:false,\n  monitored_fixture_mode:false,\n  monitored_groups:[],            // [{screen_name, category:'competitor|finance_community|city_community|entrepreneur_community', enabled, region, niche_scope, post_lookback_days, max_posts_per_group, max_relevant_posts, max_comments_per_post}]\n  monitored_max_groups_per_run:5,\n  monitored_max_posts_per_group:10,\n  monitored_max_relevant_posts:5,\n  monitored_max_comments_per_post:20,\n  monitored_max_total_comments:100,\n  monitored_post_lookback_days:14,\n  monitored_request_timeout_ms:15000,\n  monitored_max_retries:2,\n  monitored_backoff_ms:1500,\n  approval_status_for_unique:'new',\n  write_duplicate_audit:true,\n  hard_skip_debug_audit:false,\n  duplicate_next_action:'monitor_duplicate',\n  spreadsheet_id_placeholder:'PASTE_SPREADSHEET_ID',\n  agent_request_id:'wf13_req_'+stamp,\n  run_id:'wf13_'+stamp,\n  created_at:now\n}}];\n"
      },
      "id": "wf13-config",
      "name": "Set Connector Config",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -200,
        560
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "wf13-if-fixture",
              "leftValue": "={{ $json.fixture_mode }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "wf13-if",
      "name": "IF fixture_mode?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        20,
        560
      ]
    },
    {
      "parameters": {
        "jsCode": "// Fixture VK PUBLIC group posts/comments (what a guarded VK API fetch would yield). $0, no network.\n// SYNTHETIC TEST DATA ONLY: group names carry _fixture; phones use the non-routable +7 000 prefix; profiles are\n// synthetic. The mix exercises the Stage 3.5 Lead Scout path end-to-end: consumer demand comments (high intent w/\n// synthetic username, high intent w/ synthetic phone, question w/o contact, complaint/fraud-fear, business finance),\n// plus negatives that must NOT become leads (competitor ad post, market-news post, hard-negative B2B service),\n// plus an in-batch duplicate. author_profile_public marks whether the author profile URL is public \u2014 profile_url is\n// recorded ONLY when public; never reconstructed; NEVER used for outreach.\nconst items=[\n { group:'kredity_msk_fixture', group_title:'\u041a\u0440\u0435\u0434\u0438\u0442\u044b \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'post', item_id:'201', comment_id:'',\n   item_url:'https://vk.com/wall-111222_201', author_handle:'id_broker_msk_fixture', author_name:'\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0411\u0440\u043e\u043a\u0435\u0440 \u041c\u0421\u041a (fixture)', author_profile_public:true,\n   published_at:'2026-06-14T12:10:00+03:00',\n   text:'\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440: \u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432, \u0441 \u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439. \u0411\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b, \u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. \u041c\u043e\u0441\u043a\u0432\u0430 \u0438 \u041c\u041e. \u0422\u0435\u043b: +7 000 000-00-09' },\n { group:'kredity_msk_fixture', group_title:'\u041a\u0440\u0435\u0434\u0438\u0442\u044b \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'comment', item_id:'201', comment_id:'2011',\n   item_url:'https://vk.com/wall-111222_201?reply=2011', author_handle:'id_user_synthetic_1', author_name:'\u0418\u0433\u043e\u0440\u044c (fixture)', author_profile_public:true,\n   published_at:'2026-06-14T13:45:00+03:00',\n   text:'\u0421\u0440\u043e\u0447\u043d\u043e \u043d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u0430 \u0434\u0432\u0443\u0445 \u0431\u0430\u043d\u043a\u043e\u0432, \u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f. \u041a\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043c\u043e\u0433? \u041f\u0438\u0448\u0438\u0442\u0435 @synthetic_lead_1' },\n { group:'kredity_msk_fixture', group_title:'\u041a\u0440\u0435\u0434\u0438\u0442\u044b \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'comment', item_id:'201', comment_id:'2012',\n   item_url:'https://vk.com/wall-111222_201?reply=2012', author_handle:'id_user_synthetic_2', author_name:'\u041c\u0430\u0440\u0438\u043d\u0430 (fixture)', author_profile_public:true,\n   published_at:'2026-06-14T14:02:00+03:00',\n   text:'\u041d\u0443\u0436\u043d\u044b \u0434\u0435\u043d\u044c\u0433\u0438 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u041f\u0422\u0421 \u0441\u0440\u043e\u0447\u043d\u043e, \u0438\u0449\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0432 \u041c\u043e\u0441\u043a\u0432\u0435. \u041c\u043e\u0439 \u0442\u0435\u043b\u0435\u0444\u043e\u043d +7 000 000-00-01, \u043f\u0435\u0440\u0435\u0437\u0432\u043e\u043d\u0438\u0442\u0435 \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430' },\n { group:'kredity_msk_fixture', group_title:'\u041a\u0440\u0435\u0434\u0438\u0442\u044b \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'comment', item_id:'202', comment_id:'2021',\n   item_url:'https://vk.com/wall-111222_202?reply=2021', author_handle:'id_user_synthetic_3', author_name:'', author_profile_public:false,\n   published_at:'2026-06-13T09:00:00+03:00',\n   text:'\u041f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435, \u043a \u043a\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438? \u0421\u043a\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u044b\u0447\u043d\u043e \u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u043e\u0432?' },\n { group:'kredity_msk_fixture', group_title:'\u041a\u0440\u0435\u0434\u0438\u0442\u044b \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'comment', item_id:'202', comment_id:'2022',\n   item_url:'https://vk.com/wall-111222_202?reply=2022', author_handle:'id_user_synthetic_4', author_name:'\u0410\u043d\u043e\u043d\u0438\u043c (fixture)', author_profile_public:true,\n   published_at:'2026-06-13T12:00:00+03:00',\n   text:'\u042d\u0442\u0438 \u0431\u0440\u043e\u043a\u0435\u0440\u044b \u043c\u043e\u0448\u0435\u043d\u043d\u0438\u043a\u0438 \u2014 \u0432\u0437\u044f\u043b\u0438 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u0443 \u0437\u0430 \u043a\u0440\u0435\u0434\u0438\u0442 \u0438 \u043f\u0440\u043e\u043f\u0430\u043b\u0438, \u043e\u0431\u043c\u0430\u043d\u0443\u043b\u0438. \u0411\u043e\u044e\u0441\u044c \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a \u043a\u043e\u043c\u0443-\u043b\u0438\u0431\u043e.' },\n { group:'biznes_finansy_fixture', group_title:'\u0411\u0438\u0437\u043d\u0435\u0441 \u0438 \u0444\u0438\u043d\u0430\u043d\u0441\u044b (fixture)', item_type:'comment', item_id:'88', comment_id:'881',\n   item_url:'https://vk.com/wall-333444_88?reply=881', author_handle:'id_user_synthetic_5', author_name:'\u0418\u041f \u0421\u0438\u043d\u0442\u0435\u0442\u0438\u043a (fixture)', author_profile_public:true,\n   published_at:'2026-06-14T08:00:00+03:00',\n   text:'\u041d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 (\u041e\u041e\u041e), \u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430, \u0438 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0430\u044f \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f \u043f\u043e\u0434 \u0442\u0435\u043d\u0434\u0435\u0440. \u0413\u0434\u0435 \u043b\u0443\u0447\u0448\u0435 \u0432\u0437\u044f\u0442\u044c?' },\n { group:'biznes_finansy_fixture', group_title:'\u0411\u0438\u0437\u043d\u0435\u0441 \u0438 \u0444\u0438\u043d\u0430\u043d\u0441\u044b (fixture)', item_type:'post', item_id:'88', comment_id:'',\n   item_url:'https://vk.com/wall-333444_88', author_handle:'id_fin_editor_fixture', author_name:'\u0420\u0435\u0434\u0430\u043a\u0446\u0438\u044f (fixture)', author_profile_public:false,\n   published_at:'2026-06-12T09:30:00+03:00',\n   text:'\u0411\u0430\u043d\u043a\u0438 \u0443\u0436\u0435\u0441\u0442\u043e\u0447\u0438\u043b\u0438 \u0441\u043a\u043e\u0440\u0438\u043d\u0433: \u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u043f\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c \u043a\u0440\u0435\u0434\u0438\u0442\u0430\u043c \u0441\u0442\u0430\u043b\u043e \u0431\u043e\u043b\u044c\u0448\u0435. \u041e\u0431\u0437\u043e\u0440 \u0440\u044b\u043d\u043a\u0430 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430.' },\n { group:'uslugi_msk_fixture', group_title:'\u0423\u0441\u043b\u0443\u0433\u0438 \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'post', item_id:'55', comment_id:'',\n   item_url:'https://vk.com/wall-555666_55', author_handle:'id_reg_firm_fixture', author_name:'\u0420\u0435\u0433\u0424\u0438\u0440\u043c\u0430 (fixture)', author_profile_public:true,\n   published_at:'2026-06-11T10:00:00+03:00',\n   text:'\u042e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u041e\u041e\u041e \u043e\u0442 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u0430, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u041e\u041e\u041e \u0438 \u0418\u041f \u043f\u043e\u0434 \u043a\u043b\u044e\u0447, \u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440\u0441\u043a\u043e\u0435 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u0435.' },\n { group:'kredity_msk_fixture', group_title:'\u041a\u0440\u0435\u0434\u0438\u0442\u044b \u041c\u043e\u0441\u043a\u0432\u0430 (fixture)', item_type:'comment', item_id:'201', comment_id:'2011',\n   item_url:'https://vk.com/wall-111222_201?reply=2011', author_handle:'id_user_synthetic_1', author_name:'\u0418\u0433\u043e\u0440\u044c (fixture)', author_profile_public:true,\n   published_at:'2026-06-14T13:45:00+03:00',\n   text:'\u0421\u0440\u043e\u0447\u043d\u043e \u043d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u0430 \u0434\u0432\u0443\u0445 \u0431\u0430\u043d\u043a\u043e\u0432, \u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f. \u041a\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043c\u043e\u0433? \u041f\u0438\u0448\u0438\u0442\u0435 @synthetic_lead_1' }\n];\nreturn items.map(p=>({json:p}));\n"
      },
      "id": "wf13-fixture",
      "name": "Build Fixture VK Group Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        440
      ]
    },
    {
      "parameters": {
        "jsCode": "// LIVE VK APPROVAL GATE v0.3 (DEC-125/138) \u2014 fires on fixture_mode=false. INERT BY DEFAULT: throws unless the\n// operator has explicitly armed the run. Even when armed, the downstream HTTP node ships DISABLED with no\n// credential, so no network call can happen without a second deliberate operator action.\n// Supports BOTH public methods: wall.get (public group walls) and wall.getComments (PUBLIC POST comments \u2014 where\n// consumer demand lives). Hard scope (CONTACT_AND_OUTREACH_POLICY): PUBLIC VK groups/posts/comments ONLY \u2014 no\n// private messages, no closed groups, no member extraction, no hidden contacts, no auto-outreach, no people lists.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Connector Config').first().json;\nconst token=str(cfg.live_approval_token);\nconst methods=Array.isArray(cfg.live_methods)?cfg.live_methods.map(str).filter(Boolean):['wall.get'];\nconst groups=Array.isArray(cfg.live_group_allowlist)?cfg.live_group_allowlist.map(g=>str(g)).filter(Boolean):[];\nconst posts=Array.isArray(cfg.live_post_allowlist)?cfg.live_post_allowlist.map(p=>str(p)).filter(Boolean):[];\nconst maxItems=Math.min(Number(cfg.live_max_items_per_group)||10,10);\nif(token!=='I_APPROVE_LIVE_VK_PUBLIC_DISCUSSION'){\n  throw new Error('WF13 live gate: operator approval token missing. Live VK public discussion fetch requires explicit operator approval: set live_approval_token=I_APPROVE_LIVE_VK_PUBLIC_DISCUSSION, fill live_group_allowlist (public groups for wall.get) and/or live_post_allowlist (public post URLs for wall.getComments), bind a VK API credential in n8n, then ENABLE the disabled HTTP node. Default run stays fixture-only ($0). See docs/N8N_WORKFLOW_13_PUBLIC_DISCUSSION_OR_REVIEWS_CONNECTOR_RU.md.');\n}\nif(!groups.length&&!posts.length){ throw new Error('WF13 live gate: both live_group_allowlist and live_post_allowlist are empty \u2014 allowlist-only operation, no discovery crawling.'); }\n// reject anything that is not a public group/post reference: invite links, private/closed markers, phone-like ids\nconst badG=groups.filter(g=>/join|invite|im\\?sel|write\\d|@|\\s/i.test(g)||g.charAt(0)==='+');\nif(badG.length){ throw new Error('WF13 live gate: rejected non-public-group entries (invite links / private chats are forbidden): '+badG.join(', ')); }\nconst out=[];\nif(methods.indexOf('wall.get')>=0){\n  for(const g of groups){\n    const screen=g.replace(/^https?:\\/\\/(www\\.)?vk\\.com\\//i,'').replace(/[\\/?#].*$/,'');\n    out.push({ json: { method:'wall.get', fetch_url:'https://api.vk.com/method/wall.get', screen_name:screen, owner_id:'', post_id:'', max_items:maxItems, api_version:'5.199', live_transport:'vk_api' } });\n  }\n}\nif(methods.indexOf('wall.getComments')>=0){\n  for(const u of posts){\n    const m=str(u).match(/wall(-?\\d+)_(\\d+)/);\n    if(!m){ throw new Error('WF13 live gate: live_post_allowlist entry is not a public wall post URL (expected .../wall-<owner>_<post>): '+u); }\n    out.push({ json: { method:'wall.getComments', fetch_url:'https://api.vk.com/method/wall.getComments', screen_name:'', owner_id:m[1], post_id:m[2], max_items:maxItems, api_version:'5.199', live_transport:'vk_api' } });\n  }\n}\nif(!out.length){ throw new Error('WF13 live gate: no live targets produced for the selected live_methods ('+methods.join(', ')+').'); }\nreturn out;\n"
      },
      "id": "wf13-guard",
      "name": "LIVE VK Approval Gate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        700
      ]
    },
    {
      "parameters": {
        "jsCode": "// Normalize VK public posts/comments into the WF09/WF11-compatible record shape. Deterministic, $0.\n// Audience focus (DEC-121): questions/objections from public discussions become question_objection\n// records feeding WF08 -> WF10 audience_activity_signals. Contact policy (DEC-097/098/114): contact_public\n// ONLY when visible verbatim in the public text; author profile_url ONLY when the profile is public;\n// nothing reconstructed; no outreach \u2014 default manual_review.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction low(v){return str(v).toLowerCase();}\nfunction cut(s,n){s=str(s);return s.length>n?s.slice(0,n):s;}\nfunction hashText(s){s=str(s);let h=5381;for(let i=0;i<s.length;i++){h=((h<<5)+h+s.charCodeAt(i))>>>0;}return ('00000000'+h.toString(16)).slice(-8);}\nfunction canonUrl(u){u=str(u);return u.replace(/[?#].*$/,'').replace(/\\/+$/,'');}\nconst cfg=$('Set Connector Config').first().json;\nconst items=$input.all().map(i=>i.json);\n// niche vocabulary v0.1 hardcoded (credit_brokerage) \u2014 migrates to niche packs (DEC-100)\nconst STRONG=['\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440','\u0438\u043f\u043e\u0442\u0435\u0447\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440','\u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430','\u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u043c','\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f','\u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435','\u0442\u0435\u043d\u0434\u0435\u0440\u043d\u044b\u0435 \u0437\u0430\u0439\u043c','\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438','\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435','\u0441\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0432\u043a\u0438','\u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435 \u043a\u0440\u0435\u0434\u0438\u0442\u0430','\u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a\u0430'];\nconst WEAK=['\u043a\u0440\u0435\u0434\u0438\u0442','\u0437\u0430\u0439\u043c','\u0431\u0430\u043d\u043a','\u043e\u0442\u043a\u0430\u0437','\u0441\u043a\u043e\u0440\u0438\u043d\u0433','\u0441\u0442\u0430\u0432\u043a','\u0437\u0430\u0451\u043c\u0449\u0438\u043a','\u0438\u043f\u043e\u0442\u0435\u043a','\u0444\u0438\u043d\u0430\u043d\u0441','\u0431\u0440\u043e\u043a\u0435\u0440','\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442','\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a'];\nconst HARD_NEG=['\u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0434\u0440\u0435\u0441','\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043e\u043e\u043e','\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438\u043f','\u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440','\u044d\u043a\u0432\u0430\u0439\u0440\u0438\u043d\u0433','pos-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b','\u043a\u0430\u0441\u0441\u0430','\u043f\u0435\u0447\u0430\u0442\u044c \u0438 \u0448\u0442\u0430\u043c\u043f','\u044d\u0446\u043f','\u0430\u0440\u0435\u043d\u0434\u0430 \u043e\u0444\u0438\u0441\u0430','\u043a\u043e\u0432\u043e\u0440\u043a\u0438\u043d\u0433'];\nconst QUESTION=['\u043f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435','\u043f\u043e\u0441\u043e\u0432\u0435\u0442\u0443\u0439\u0442\u0435','\u043a\u0442\u043e \u0431\u0440\u0430\u043b','\u043a\u0442\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u043b\u0441\u044f','\u043a \u043a\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f','\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e \u0441\u0442\u043e\u0438\u0442','\u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c','\u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c','?'];\nconst OBJECTION=['\u043c\u043e\u0448\u0435\u043d\u043d\u0438\u043a','\u043e\u0431\u043c\u0430\u043d','\u043a\u0438\u0434\u0430\u043b','\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u0443 \u0438 \u043f\u0440\u043e\u043f\u0430\u0434\u0430','\u043d\u0435 \u0432\u0435\u0440\u044e','\u0440\u0430\u0437\u0432\u043e\u0434','\u0431\u043e\u044e\u0441\u044c'];\n// consumer DEMAND markers (Stage 3.5): a member publicly asking/needing -> audience lead signal (not a question shape)\nconst DEMAND=['\u043d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442','\u043d\u0443\u0436\u0435\u043d \u0437\u0430\u0439\u043c','\u043d\u0443\u0436\u043d\u0430 \u0438\u043f\u043e\u0442\u0435\u043a','\u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441','\u043d\u0443\u0436\u043d\u044b \u0434\u0435\u043d\u044c\u0433\u0438','\u0438\u0449\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0430','\u0438\u0449\u0443 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0433\u043e \u0431\u0440\u043e\u043a\u0435\u0440\u0430','\u043f\u043e\u043c\u043e\u0433\u0438\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c','\u043f\u043e\u043c\u043e\u0433\u0438\u0442\u0435 \u0432\u0437\u044f\u0442\u044c','\u0445\u043e\u0447\u0443 \u0432\u0437\u044f\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442','\u0434\u0435\u043d\u044c\u0433\u0438 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433','\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043f\u0442\u0441','\u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u043d\u0443\u0436\u043d\u0430 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0430\u044f \u0433\u0430\u0440\u0430\u043d\u0442\u0438','\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437'];\nconst KW=[['\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440','\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440'],['\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432'],['\u043f\u043b\u043e\u0445','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f'],['\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b','\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b'],['\u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442','\u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'],['\u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u0440\u0435\u0434\u0438\u0442'],['\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440','\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435'],['\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a','\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a\u0438'],['\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442','\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u0430 (\u0434\u043e\u0432\u0435\u0440\u0438\u0435)'],['\u043c\u043e\u0448\u0435\u043d\u043d\u0438\u043a','\u043d\u0435\u0434\u043e\u0432\u0435\u0440\u0438\u0435 \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0430\u043c'],['\u043e\u0442\u043a\u0430\u0437','\u043e\u0442\u043a\u0430\u0437\u044b \u0431\u0430\u043d\u043a\u043e\u0432']];\n// Defect C: probable_need must reflect ACTUAL evidence \u2014 never inherit a refusal hint without refusal evidence.\nfunction probableNeed(blob,rth){\n  if(rth!=='question_objection')return '';\n  if(blob.indexOf('\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437')>=0||blob.indexOf('\u043e\u0442\u043a\u0430\u0437\u0430\u043b\u0438')>=0||blob.indexOf('\u0431\u0430\u043d\u043a \u043e\u0442\u043a\u0430\u0437')>=0||blob.indexOf('\u043e\u0442\u043a\u0430\u0437 \u0431\u0430\u043d\u043a')>=0)return '\u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u043c \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u0431\u0430\u043d\u043a\u043e\u0432';\n  if(blob.indexOf('\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043f\u0442\u0441')>=0||blob.indexOf('\u043f\u0442\u0441')>=0||blob.indexOf('\u0437\u0430\u043b\u043e\u0433 \u0430\u0432\u0442\u043e')>=0)return '\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u041f\u0422\u0421';\n  if(blob.indexOf('\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043d\u0435\u0434\u0432\u0438\u0436')>=0||blob.indexOf('\u0437\u0430\u043b\u043e\u0433 \u043d\u0435\u0434\u0432\u0438\u0436')>=0)return '\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043d\u0435\u0434\u0432\u0438\u0436\u0438\u043c\u043e\u0441\u0442\u0438';\n  if(blob.indexOf('\u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430')>=0||blob.indexOf('\u043e\u0431\u043e\u0440\u043e\u0442\u043d')>=0||blob.indexOf('\u0442\u0435\u043d\u0434\u0435\u0440')>=0||blob.indexOf('\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a \u0433\u0430\u0440\u0430\u043d\u0442\u0438')>=0||blob.indexOf('\u0434\u043b\u044f \u043e\u043e\u043e')>=0||blob.indexOf('\u0434\u043b\u044f \u0438\u043f')>=0)return '\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0438\u0437\u043d\u0435\u0441\u0430 (\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435/\u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f/\u0442\u0435\u043d\u0434\u0435\u0440)';\n  if(blob.indexOf('\u0438\u043f\u043e\u0442\u0435\u043a')>=0)return '\u0438\u043f\u043e\u0442\u0435\u043a\u0430';\n  if(blob.indexOf('\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441')>=0||blob.indexOf('\u0441\u043d\u0438\u0437\u0438\u0442\u044c \u0441\u0442\u0430\u0432\u043a')>=0)return '\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435';\n  if(blob.indexOf('\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d')>=0||blob.indexOf('\u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d')>=0||blob.indexOf('\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0438')>=0)return '\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u0440\u0438 \u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438';\n  return '';\n}\nconst out=[];\nfor(const p of items){\n  const isComment=(low(p.item_type)==='comment');\n  const text=str(p.text);\n  const blob=low(text)+' '+low(p.group_title);\n  const valid=!!(str(p.group)&&str(p.item_id)&&text.length>=30);\n  const strongHit=STRONG.filter(t=>blob.indexOf(t)>=0);\n  const weakHit=WEAK.filter(t=>blob.indexOf(t)>=0);\n  const negHit=HARD_NEG.filter(t=>blob.indexOf(t)>=0);\n  const qHit=QUESTION.filter(t=>blob.indexOf(t)>=0);\n  const objHit=OBJECTION.filter(t=>blob.indexOf(t)>=0);\n  const demHit=DEMAND.filter(t=>blob.indexOf(t)>=0);\n  let record_type_hint='irrelevant';let touchpoint_type='irrelevant_source';let predicted_entity_type='irrelevant';let predicted_route='skipped_log';\n  let lead_intent='none';let confidence=10;let hard_skip=false;let relevance_reason='';\n  const looksAd=/\u0442\u0435\u043b[:.]|\\+7|\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b|\u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|\u043f\u043e\u0434 \u043a\u043b\u044e\u0447/i.test(text)&&!isComment;\n  if(!valid){ relevance_reason='invalid: missing group/item_id or text<30'; }\n  else if(negHit.length&&!strongHit.length){ hard_skip=true; relevance_reason='hard negative: '+negHit.join(', '); }\n  else if(strongHit.length&&looksAd){ record_type_hint='competitor_activity';touchpoint_type='competitor_content_post';predicted_entity_type='competitor';predicted_route='monitor_queue';confidence=70;relevance_reason='strong competitor ad: '+strongHit.slice(0,4).join(', '); }\n  else if((demHit.length||qHit.length||objHit.length)&&(strongHit.length||weakHit.length>=2||demHit.length)){ record_type_hint='question_objection';touchpoint_type='forum_discussion';predicted_entity_type='lead_signal';predicted_route='review_queue';lead_intent=demHit.length?'high':(objHit.length&&!qHit.length?'low':'medium');confidence=demHit.length?65:(objHit.length&&!qHit.length?50:60);relevance_reason=(demHit.length?('demand: '+demHit.slice(0,3).join(', ')):'')+(qHit.length?((demHit.length?'; ':'')+'question: '+qHit.slice(0,3).join(', ')):'')+(objHit.length?(((demHit.length||qHit.length)?'; ':'')+'objection: '+objHit.slice(0,3).join(', ')):''); }\n  else if(strongHit.length){ record_type_hint='market_signal';predicted_entity_type='market_signal';touchpoint_type='forum_discussion';predicted_route='content_queue';confidence=50;relevance_reason='strong niche terms, no ad/question shape: '+strongHit.slice(0,4).join(', '); }\n  else if(weakHit.length>=2){ record_type_hint='market_signal';predicted_entity_type='market_signal';touchpoint_type='forum_discussion';predicted_route='content_queue';confidence=45;relevance_reason='weak finance: '+weakHit.slice(0,4).join(', '); }\n  else { hard_skip=true; relevance_reason='no niche evidence'; }\n  // VK comments are their own public touchpoint class (DEC-125): any business-relevant COMMENT row\n  // carries touchpoint_type=public_comment (posts keep competitor_content_post / forum_discussion).\n  if(isComment&&!hard_skip&&record_type_hint!=='irrelevant'){ touchpoint_type='public_comment'; }\n  // public contact: verbatim phone/@handle in the PUBLIC text only \u2014 never reconstructed (DEC-114 channel category).\n  let contact='';let contactChannel='';let contactFormat='';\n  const ph=text.match(/(?:\\+7|8)[\\s\\-(]*\\d{3}[\\s\\-)]*\\d{3}[\\s\\-]*\\d{2}[\\s\\-]*\\d{2}/);\n  const h=text.match(/@[a-zA-Z0-9_]{5,32}/);\n  if(ph){contact=ph[0];contactChannel='phone';contactFormat='phone';}\n  else if(h){contact=h[0];contactChannel='telegram';contactFormat='handle';}\n  const kws=[];const seen={};for(const k of KW){if(blob.indexOf(k[0])>=0&&!seen[k[1]]){seen[k[1]]=1;kws.push(k[1]);}}\n  let service='unknown';\n  if(blob.indexOf('\u0438\u043f\u043e\u0442\u0435\u043a')>=0||blob.indexOf('\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440')>=0)service='mortgage_refinance';\n  else if(blob.indexOf('\u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430')>=0||blob.indexOf('\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435')>=0||blob.indexOf('\u0442\u0435\u043d\u0434\u0435\u0440')>=0||blob.indexOf('\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a \u0433\u0430\u0440\u0430\u043d\u0442\u0438')>=0)service='business_credit';\n  else if(blob.indexOf('\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043f\u0442\u0441')>=0||blob.indexOf('\u043f\u0442\u0441')>=0||blob.indexOf('\u0437\u0430\u043b\u043e\u0433 \u0430\u0432\u0442\u043e')>=0||blob.indexOf('\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433')>=0)service='pts_loan';\n  else if(blob.indexOf('\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432')>=0||blob.indexOf('\u043e\u0442\u043a\u0430\u0437')>=0)service='credit_after_refusals';\n  if(blob.indexOf('\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440')>=0)service='credit_broker';\n  const canon=canonUrl(p.item_url)+(isComment?('#reply'+str(p.comment_id)):'');\n  const publicProfile=(p.author_profile_public===true);\n  out.push({ json: {\n    is_valid_listing:valid,\n    hard_skip:hard_skip,\n    relevance_reason:relevance_reason,\n    source_type:isComment?(str(cfg.source_type_comment)||'social_comment'):(str(cfg.source_type_post)||'social_post'),\n    platform:str(cfg.platform)||'vk',\n    source_url:'https://vk.com/'+str(p.group),\n    post_url:canon,\n    profile_url:publicProfile?('https://vk.com/'+str(p.author_handle)):'',\n    profile_name:str(p.author_name),\n    author_handle:str(p.author_handle),\n    author_key:str(p.author_handle)||str(p.author_name),\n    published_at:str(p.published_at),\n    region_hint:(blob.indexOf('\u043c\u043e\u0441\u043a\u0432')>=0||blob.indexOf(' \u043c\u043e')>=0?'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e':''),\n    service_hint:service,\n    query:'allowlist:'+str(p.group),\n    text_context:isComment?('\u041e\u0431\u0441\u0443\u0436\u0434\u0435\u043d\u0438\u0435 \u0432 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0439 \u0433\u0440\u0443\u043f\u043f\u0435 VK '+str(p.group_title)+', \u043f\u043e\u0441\u0442 '+canonUrl(p.item_url)):cut(text,3500),\n    comment_text:isComment?cut(text,3500):'',\n    contact_public:contact,\n    contact_channel:contactChannel,\n    contact_format:contactFormat,\n    dedup_key:'vk::'+(isComment?'social_comment':'social_post')+'::'+canon,\n    text_hash:hashText(low(text)),\n    record_type_hint:record_type_hint,\n    touchpoint_type:touchpoint_type,\n    lead_intent_hint:lead_intent,\n    urgency_hint:'none',\n    interest_topic:(record_type_hint==='competitor_activity')?'competitor VK ad copy':(record_type_hint==='question_objection'?'audience pain / question':(record_type_hint==='market_signal'?'market context':'')),\n    probable_need:probableNeed(blob,record_type_hint),\n    competitor_related:(record_type_hint==='competitor_activity'),\n    competitor_name:(record_type_hint==='competitor_activity')?str(p.author_name):'',\n    semantic_keywords:kws.join(', '),\n    ad_channel_hint:'vk',\n    confidence_score:confidence,\n    lead_temperature:(record_type_hint==='question_objection'&&lead_intent==='medium')?'warm':'none',\n    next_action:(record_type_hint==='question_objection')?'investigate':'monitor',\n    responsible:'operator',\n    estimated_analysis_cost_usd:0,\n    manager_note:'',\n    predicted_route:predicted_route,\n    predicted_entity_type:predicted_entity_type,\n    contact_evidence_url:contact?canon:'',\n    notes:''\n  }});\n}\nreturn out;"
      },
      "id": "wf13-normalize",
      "name": "Normalize VK Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        440
      ]
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "documentId": {
          "__rl": true,
          "value": "PASTE_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "market_record_registry",
          "mode": "name"
        },
        "options": {}
      },
      "id": "wf13-read-reg",
      "name": "Read market_record_registry",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        680,
        440
      ],
      "alwaysOutputData": true,
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Dedup vs market_record_registry (dedup_key) + within batch. Mirrors WF09/WF11: invalid and\n// hard-skipped items never register, never count unique, never consume the pipeline cap.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Connector Config').first().json;\nconst approvalUnique=str(cfg.approval_status_for_unique)||'new';\nconst dupNext=str(cfg.duplicate_next_action)||'monitor_duplicate';\nconst pipelineLimit=Number(cfg.pipeline_limit)||10;\nconst recs=$('Normalize VK Items').all().map(i=>i.json);\nlet reg=[];\ntry{reg=$('Read market_record_registry').all().map(i=>i.json).filter(Boolean);}catch(e){reg=[];}\nconst regSet=new Set(reg.map(r=>str(r&&r.dedup_key)).filter(Boolean));\nconst seen=new Set();\nconst out=[];\nlet acceptedCount=0;\nfor(const r of recs){\n  if(r.is_valid_listing===false){ out.push({ json: Object.assign({}, r, { dedup_status:'invalid', approval_status:'invalid', next_action:'ignore' }) }); continue; }\n  if(r.hard_skip===true){ out.push({ json: Object.assign({}, r, { dedup_status:'hard_skipped', approval_status:'skipped_irrelevant', next_action:'ignore' }) }); continue; }\n  if(acceptedCount>=pipelineLimit){ out.push({ json: Object.assign({}, r, { dedup_status:'over_pipeline_limit', approval_status:'skipped', next_action:'ignore' }) }); continue; }\n  acceptedCount++;\n  const key=str(r.dedup_key);\n  let dedup_status='unique';let approval_status=approvalUnique;let next_action=str(r.next_action);\n  if(key&&regSet.has(key)){ dedup_status='duplicate_in_registry';approval_status='duplicate';next_action=dupNext; }\n  else if(key&&seen.has(key)){ dedup_status='duplicate_in_batch';approval_status='duplicate';next_action=dupNext; }\n  else { dedup_status='unique';if(key)seen.add(key); }\n  out.push({ json: Object.assign({}, r, { dedup_status:dedup_status, approval_status:approval_status, next_action:next_action }) });\n}\nreturn out;"
      },
      "id": "wf13-dedup",
      "name": "Deduplicate Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        440
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build raw_market_records rows (exactly 40 columns, matching WF07/WF09/WF11 schema). No external calls.\nfunction str(v){return v==null?'':String(v).trim();}\n// Sheets formula-injection / #ERROR! fix (DEC-124): values starting with + or = are parsed as formulas\n// by Google Sheets (USER_ENTERED) \u2014 '+7 ...' phones become #ERROR!. A leading apostrophe forces text.\nfunction sheetsSafeText(v){v=str(v);return /^[+=]/.test(v)?(\"'\"+v):v;}\nfunction moscowIsoNow(){var m=new Date(Date.now()+10800000);return m.toISOString().replace('Z','+03:00');}\nfunction moscowStamp(){var z=function(n){return String(n).padStart(2,'0');};var m=new Date(Date.now()+10800000);return m.getUTCFullYear()+z(m.getUTCMonth()+1)+z(m.getUTCDate())+'_'+z(m.getUTCHours())+z(m.getUTCMinutes())+z(m.getUTCSeconds());}\nconst cfg=$('Set Connector Config').first().json;\nconst reqId=str(cfg.agent_request_id);\nconst created=str(cfg.created_at);\nconst stamp=str(cfg.run_id).replace(/^wf13_/,'')||moscowStamp();\nconst writeDupAudit=(cfg.write_duplicate_audit===true||str(cfg.write_duplicate_audit).toLowerCase()==='true');\nconst hardDebug=(cfg.hard_skip_debug_audit===true||str(cfg.hard_skip_debug_audit).toLowerCase()==='true');\nconst recs=$('Deduplicate Items').all().map(i=>i.json);\nconst out=[];let idx=0;\nfor(const r of recs){\n  idx++;\n  const ds0=str(r.dedup_status);\n  if(ds0==='invalid'||ds0==='over_pipeline_limit')continue;\n  if(ds0==='hard_skipped'&&!hardDebug)continue;\n  if(!writeDupAudit && ds0!=='unique')continue;\n  let notes='stage_3_source_foundation_vk_public_discussion';\n  if(str(r.contact_public)){ notes+='; contact_format='+(str(r.contact_format)||'unknown')+'; contact_source_url='+str(r.contact_evidence_url)+'; contact_use_policy=manual_review (CONTACT_AND_OUTREACH_POLICY)'; }\n  out.push({ json: {\n    record_id:'wf13_rec_'+stamp+'_'+idx,\n    agent_request_id:reqId,\nsource_run_id:str(cfg.run_id)||reqId,\ndata_mode:str(cfg.data_mode)||\"live\",\nquality_status:\"healthy\",\nreport_eligible:true,\nreview_status:\"confirmed\",\nquality_flags:\"\",\n    created_at:created,\n    source_type:str(r.source_type),\n    platform:str(r.platform),\n    source_url:str(r.source_url),\n    post_url:str(r.post_url),\n    profile_url:str(r.profile_url),\n    profile_name:str(r.profile_name),\n    author_handle:str(r.author_handle),\n    published_at:str(r.published_at),\n    region_hint:str(r.region_hint),\n    service_hint:str(r.service_hint),\n    query:str(r.query),\n    text_context:str(r.text_context),\n    comment_text:str(r.comment_text),\n    contact_public:sheetsSafeText(r.contact_public),\n    contact_channel:str(r.contact_channel),\n    dedup_key:str(r.dedup_key),\n    record_type_hint:str(r.record_type_hint),\n    touchpoint_type:str(r.touchpoint_type),\n    lead_intent_hint:str(r.lead_intent_hint),\n    urgency_hint:str(r.urgency_hint),\n    interest_topic:str(r.interest_topic),\n    probable_need:str(r.probable_need),\n    competitor_related:(r.competitor_related===true||str(r.competitor_related).toLowerCase()==='true'),\n    competitor_name:str(r.competitor_name),\n    semantic_keywords:str(r.semantic_keywords),\n    ad_channel_hint:str(r.ad_channel_hint),\n    confidence_score:Number(r.confidence_score)||0,\n    lead_temperature:str(r.lead_temperature),\n    next_action:str(r.next_action),\n    responsible:str(r.responsible),\n    dedup_status:str(r.dedup_status),\n    approval_status:str(r.approval_status),\n    approved_by:'',\n    approved_at:'',\n    estimated_analysis_cost_usd:Number(r.estimated_analysis_cost_usd)||0,\n    manager_note:str(r.manager_note),\n    notes:notes\n  }});\n}\nreturn out;"
      },
      "id": "wf13-raw",
      "name": "Build raw_market_records Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "PASTE_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "raw_market_records",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "wf13-app-raw",
      "name": "Append raw_market_records",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1340,
        300
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Build market_record_registry rows for UNIQUE records only (exactly 15 columns). No external calls.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction moscowIsoNow(){var m=new Date(Date.now()+10800000);return m.toISOString().replace('Z','+03:00');}\nconst now=moscowIsoNow();\nconst recs=$('Deduplicate Items').all().map(i=>i.json);\nconst out=[];\nfor(const r of recs){\n  if(str(r.dedup_status)!=='unique')continue;\n  out.push({ json: {\n    dedup_key:str(r.dedup_key),\n    source_type:str(r.source_type),\n    platform:str(r.platform),\n    source_url:str(r.source_url),\n    post_url:str(r.post_url),\n    profile_url:str(r.profile_url),\n    author_handle:str(r.author_handle),\n    text_hash:str(r.text_hash),\n    first_seen_at:now,\n    last_seen_at:now,\n    last_route:str(r.predicted_route),\n    last_processing_status:'raw_collected',\n    last_entity_type:str(r.predicted_entity_type),\n    agent_request_id:str($('Set Connector Config').first().json.agent_request_id),\n    note:'stage_3_source_foundation_vk_public_discussion'\n  }});\n}\nreturn out;"
      },
      "id": "wf13-regrows",
      "name": "Build market_record_registry Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        200
      ]
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "PASTE_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "market_record_registry",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "wf13-app-reg",
      "name": "Append market_record_registry",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1780,
        200
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// One agent_requests row (exactly 21 columns). status=completed. No external calls.\n// Audience aggregates (DEC-121, aggregate-only): active_author_count / repeat_author_count computed over\n// business-relevant items \u2014 counts only, never a member list, never outreach targets.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction cut(s,n){s=str(s);return s.length>n?s.slice(0,n):s;}\nconst cfg=$('Set Connector Config').first().json;\nconst recs=$('Deduplicate Items').all().map(i=>i.json);\nconst items_received=recs.length;\nconst invalid_items=recs.filter(r=>str(r.dedup_status)==='invalid').length;\nconst structurally_valid_items=items_received-invalid_items;\nconst hard_skipped_items=recs.filter(r=>str(r.dedup_status)==='hard_skipped').length;\nconst business_relevant_items=structurally_valid_items-hard_skipped_items;\nconst over_pipeline_limit=recs.filter(r=>str(r.dedup_status)==='over_pipeline_limit').length;\nconst unique=recs.filter(r=>str(r.dedup_status)==='unique').length;\nconst duplicate=recs.filter(r=>str(r.dedup_status).indexOf('duplicate')===0).length;\nconst authorCounts={};\n// Defect F: AUDIENCE aggregates = consumer/audience authors only (record_type_hint==='question_objection').\n// Competitor (competitor_activity) & editorial (market_signal) accounts are NOT audience authors. Counted over\n// UNIQUE rows only so duplicates can't inflate repeats. Counts only \u2014 never a member list, never outreach targets.\nfor(const r of recs){ if(str(r.dedup_status)!=='unique')continue; if(str(r.record_type_hint)!=='question_objection')continue; const a=str(r.author_key); if(a)authorCounts[a]=(authorCounts[a]||0)+1; }\nconst audience_author_count=Object.keys(authorCounts).length;\nconst repeat_audience_author_count=Object.keys(authorCounts).filter(a=>authorCounts[a]>=2).length;\nconst question_items=recs.filter(r=>str(r.record_type_hint)==='question_objection'&&str(r.dedup_status)==='unique').length;\nconst fixtureMode=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nconst groups=Array.isArray(cfg.group_allowlist)?cfg.group_allowlist.join(', '):'';\nlet notes='WF13 foundation (DEC-121): VK PUBLIC group post/comment connector, fixture-first, no external calls, no private chats/messages, no member extraction. Canonical handoff: raw_market_records -> WF14 (Lead Scout) -> public_lead_signals (manual, no auto-handoff); WF08 optional Stage 3 path. Source: '+(fixtureMode?'fixture':'live')+'. Audience aggregates are AUDIENCE-author counts only (audience_author_count='+audience_author_count+', repeat_audience_author_count='+repeat_audience_author_count+'; consumer comment authors only, excludes competitor/editor accounts) \u2014 no member lists, no outreach. Contact policy: public contacts verbatim from text only, author profile URL only if public, default manual_review.';\nif(hard_skipped_items>0){\n  const firstHard=recs.filter(r=>str(r.dedup_status)==='hard_skipped')[0];\n  notes+=' hard_skipped_items='+hard_skipped_items+'.';\n  if(firstHard){ notes+=' first_hard_skip: '+cut(str(firstHard.post_url),80)+' ['+cut(str(firstHard.relevance_reason),120)+']'; }\n}\nreturn [{ json: {\n  agent_request_id:str(cfg.agent_request_id),\n  created_at:str(cfg.created_at),\n  requested_by:str(cfg.requested_by)||'operator',\n  request_text:'VK public group post/comment discovery (allowlist): '+groups,\n  request_type:str(cfg.request_type)||'social_source_discovery',\n  source_scope:'social_public',\n  platforms:'vk',\n  query:'allowlist: '+groups,\n  region:str(cfg.region)||'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e',\n  service_focus:str(cfg.service_focus)||'credit_broker',\n  requested_limit:Number(cfg.pipeline_limit)||10,\n  status:'completed',\n  plan_summary:'WF13 VK public discussion connector ('+(fixtureMode?'fixture mode':'live mode')+') -> raw_market_records + market_record_registry for the Lead Scout engine (WF14 -> public_lead_signals). Canonical Lead Scout handoff = WF14; WF08 Touchpoint Analyzer remains an OPTIONAL Stage 3 analytical path, not the mandatory handoff. One source at a time (DEC-096).',\n  estimated_source_cost_usd:0,\n  estimated_analysis_cost_usd:0,\n  approval_required:false,\n  approved_by:'',\n  approved_at:'',\n  result_summary:'items_received='+items_received+'; structurally_valid_items='+structurally_valid_items+'; invalid_items='+invalid_items+'; business_relevant_items='+business_relevant_items+'; hard_skipped_items='+hard_skipped_items+'; unique='+unique+'; duplicates='+duplicate+'; over_pipeline_limit='+over_pipeline_limit+'; question_objection_unique='+question_items+'; audience_author_count='+audience_author_count+'; repeat_audience_author_count='+repeat_audience_author_count+'.',\n  next_action:(unique>0?'Inspect raw_market_records, verify relevance, then run WF14 (Lead Scout triage) manually with source_agent_request_id='+str(cfg.agent_request_id)+' to isolate this run -> public_lead_signals. WF08 = OPTIONAL Stage 3 analytical path, not the Lead Scout handoff. No auto-handoff, no outreach (DEC-098).':'No unique business-relevant items: review fixtures/allowlist; do NOT run WF14.'),\n  notes:notes\n}}];"
      },
      "id": "wf13-ar",
      "name": "Build agent_requests Row",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        440
      ]
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "PASTE_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "agent_requests",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "wf13-app-ar",
      "name": "Append agent_requests",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1780,
        440
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Final run summary. No external calls.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Connector Config').first().json;\nconst recs=$('Deduplicate Items').all().map(i=>i.json);\nconst items_received=recs.length;\nconst invalid_items=recs.filter(r=>str(r.dedup_status)==='invalid').length;\nconst structurally_valid_items=items_received-invalid_items;\nconst hard_skipped_items=recs.filter(r=>str(r.dedup_status)==='hard_skipped').length;\nconst business_relevant_items=structurally_valid_items-hard_skipped_items;\nconst over_pipeline_limit=recs.filter(r=>str(r.dedup_status)==='over_pipeline_limit').length;\nconst unique=recs.filter(r=>str(r.dedup_status)==='unique').length;\nconst duplicate=recs.filter(r=>str(r.dedup_status).indexOf('duplicate')===0).length;\nconst writeDupAudit=(cfg.write_duplicate_audit===true||str(cfg.write_duplicate_audit).toLowerCase()==='true');\nconst rawWritten=writeDupAudit?(unique+duplicate):unique;\nconst record_type_counts={};const authorCounts={};\nfor(const r of recs){\n  if(['invalid','over_pipeline_limit','hard_skipped'].indexOf(str(r.dedup_status))>=0)continue;\n  const t=str(r.record_type_hint)||'unknown';record_type_counts[t]=(record_type_counts[t]||0)+1;\n  // Defect F: AUDIENCE authors only (consumer question_objection rows); excludes competitor/editor accounts.\n  if(str(r.dedup_status)!=='unique')continue;\n  if(str(r.record_type_hint)!=='question_objection')continue;\n  const a=str(r.author_key);if(a)authorCounts[a]=(authorCounts[a]||0)+1;\n}\nreturn [{ json: {\n  workflow:'13 - Public Discussion Connector Foundation (VK Public Groups/Posts/Comments)',\n  agent_request_id:str(cfg.agent_request_id),\n  run_id:str(cfg.run_id),\n  fixture_mode:(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true'),\n  live_mode:(cfg.live_mode===true||str(cfg.live_mode).toLowerCase()==='true'),\n  items_received:items_received,\n  structurally_valid_items:structurally_valid_items,\n  invalid_items:invalid_items,\n  business_relevant_items:business_relevant_items,\n  hard_skipped_items:hard_skipped_items,\n  over_pipeline_limit:over_pipeline_limit,\n  unique_count:unique,\n  duplicate_count:duplicate,\n  raw_market_records_written:rawWritten,\n  registry_rows_written:unique,\n  record_type_counts:record_type_counts,\n  audience_author_count:Object.keys(authorCounts).length,\n  repeat_audience_author_count:Object.keys(authorCounts).filter(a=>authorCounts[a]>=2).length,\n  source_cost_usd:0,\n  next_action:(unique>0?'Inspect raw_market_records, verify relevance, then run WF14 (Lead Scout triage) manually -> public_lead_signals. WF08 = optional Stage 3 analytical path, not the Lead Scout handoff. No auto-handoff, no outreach.':'No unique business-relevant items: review fixtures/allowlist; do NOT run WF14.')\n}}];"
      },
      "id": "wf13-final",
      "name": "Final Summary Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        440
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.fetch_url }}",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "domain",
              "value": "={{ $json.screen_name }}"
            },
            {
              "name": "owner_id",
              "value": "={{ $json.owner_id }}"
            },
            {
              "name": "post_id",
              "value": "={{ $json.post_id }}"
            },
            {
              "name": "count",
              "value": "={{ $json.max_items }}"
            },
            {
              "name": "v",
              "value": "={{ $json.api_version }}"
            },
            {
              "name": "access_token",
              "value": "BIND_N8N_VK_CREDENTIAL_NEVER_PASTE_TOKEN"
            }
          ]
        },
        "options": {
          "timeout": 15000
        }
      },
      "id": "wf13-live-http-placeholder",
      "name": "Fetch VK Public Wall (DISABLED placeholder)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        460,
        820
      ],
      "disabled": true,
      "notes": "INERT (DEC-125/138): stays disabled until explicit operator approval. VK OFFICIAL API on PUBLIC groups/posts only \u2014 wall.get (uses domain) or wall.getComments (uses owner_id+post_id). access_token value is a placeholder string; replace with an n8n credential. No private messages, no closed groups, no member extraction."
    },
    {
      "parameters": {
        "jsCode": "// PARSE LIVE VK ITEMS (inert until the HTTP placeholder is enabled by the operator).\n// With the HTTP node disabled, items pass through WITHOUT a VK API response body \u2014 this node then throws,\n// guaranteeing no fabricated posts/comments can enter the pipeline. Handles BOTH methods:\n//   wall.get        -> resp.items are POSTS\n//   wall.getComments -> resp.items are COMMENTS (public consumer demand)\n// PUBLIC data only: profile_url is derivable only for user authors (from_id>0); no member extraction, no hidden\n// contacts, aggregate author counts happen downstream.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction msk(ts){var m=new Date(ts*1000+10800000);return m.toISOString().replace('Z','+03:00');}\nconst out=[];\nfor(const it of $input.all()){\n  const j=it.json;\n  const resp=j.response||((j.body||{}).response);\n  if(!resp||!Array.isArray(resp.items)){\n    throw new Error('WF13 live parse: no VK API response received \u2014 the HTTP placeholder node is DISABLED (inert by design, DEC-125). Enable it with a bound credential only after operator approval; default runs use fixture mode.');\n  }\n  const method=str(j.method)||'wall.get';\n  const maxItems=Math.min(Number(j.max_items)||10,10);\n  let count=0;\n  if(method==='wall.getComments'){\n    const ownerId=str(j.owner_id);          // e.g. -111222\n    const postId=str(j.post_id);            // e.g. 301\n    const group='wall'+ownerId+'_'+postId;\n    for(const c of resp.items){\n      if(count>=maxItems)break;\n      const text=str(c.text); if(!text)continue; count++;\n      const fromId=Number(c.from_id)||0;\n      out.push({ json: {\n        group:group, group_title:group, item_type:'comment', item_id:postId, comment_id:String(c.id||''),\n        item_url:'https://vk.com/wall'+ownerId+'_'+postId+'?reply='+String(c.id||''),\n        author_handle:(fromId>0?('id'+fromId):('club'+Math.abs(fromId))),\n        author_name:'', author_profile_public:(fromId>0),\n        published_at:(c.date?msk(Number(c.date)):''), text:text } });\n    }\n  } else {\n    const group=str(j.screen_name)||'unknown';\n    for(const p of resp.items){\n      if(count>=maxItems)break;\n      const text=str(p.text); if(!text)continue; count++;\n      const ownerId=String(p.owner_id||'');\n      const fromId=Number(p.from_id)||0;\n      out.push({ json: {\n        group:group, group_title:group, item_type:'post', item_id:String(p.id||''), comment_id:'',\n        item_url:'https://vk.com/wall'+ownerId+'_'+String(p.id||''),\n        author_handle:(fromId>0?('id'+fromId):('club'+Math.abs(fromId))),\n        author_name:'', author_profile_public:(fromId>0),\n        published_at:(p.date?msk(Number(p.date)):''), text:text } });\n    }\n  }\n}\nif(!out.length){ throw new Error('WF13 live parse: 0 usable items parsed \u2014 check allowlist / API response before retrying.'); }\nreturn out;\n"
      },
      "id": "wf13-live-parse-placeholder",
      "name": "Parse Live VK Items (inert)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        820
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build ONE live_source_runs row (23 columns, docs/TABLE_SCHEMA.md) \u2014 run observability (DEC-126).\n// Logs what ran, which source, counters, cost and what the operator should do next. Never logs the\n// approval token value itself \u2014 only whether one was used.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Connector Config').first().json;\nconst recs=$('Deduplicate Items').all().map(i=>i.json);\nconst fixtureMode=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nconst invalid=recs.filter(r=>str(r.dedup_status)==='invalid').length;\nconst hard=recs.filter(r=>str(r.dedup_status)==='hard_skipped').length;\nconst unique=recs.filter(r=>str(r.dedup_status)==='unique').length;\nconst dup=recs.filter(r=>str(r.dedup_status).indexOf('duplicate')===0).length;\nconst writeDupAudit=(cfg.write_duplicate_audit===true||str(cfg.write_duplicate_audit).toLowerCase()==='true');\nconst allow=fixtureMode?(Array.isArray(cfg.group_allowlist)?cfg.group_allowlist:[]):(Array.isArray(cfg.live_group_allowlist)?cfg.live_group_allowlist:[]);\nreturn [{ json: {\n  run_id:str(cfg.run_id),\n  created_at:str(cfg.created_at),\n  workflow:'WF13 - Public Discussion Connector (VK)',\n  source_family:'public_discussion',\n  platform:'vk',\n  mode:fixtureMode?'fixture':'live',\n  approval_token_used:fixtureMode?'no':(str(cfg.live_approval_token)?'yes':'no'),\n  source_allowlist:allow.join(', '),\n  max_items:Number(fixtureMode?cfg.pipeline_limit:cfg.live_max_items_per_group)||10,\n  items_received:recs.length,\n  items_relevant:recs.length-invalid-hard,\n  items_written_raw:writeDupAudit?(unique+dup):unique,\n  items_unique:unique,\n  items_duplicate:dup,\n  hard_skipped:hard,\n  external_calls:fixtureMode?0:allow.length,\n  estimated_source_cost_usd:0,\n  llm_calls:0,\n  estimated_llm_cost_usd:0,\n  status:'completed',\n  error_summary:'',\n  operator_next_action:(unique>0?'Inspect raw_market_records, verify relevance, then run WF14 Lead Scout triage manually (source_agent_request_id='+str(cfg.agent_request_id)+') -> public_lead_signals. WF08 = optional Stage 3 analytical path.':'No unique items; review allowlist/fixtures; do NOT run WF14.'),\n  notes:'stage_3_source_foundation_vk_public_discussion; public groups/posts/comments only; aggregate author counts only; no member lists; no outreach.'\n}}];"
      },
      "id": "wf13-runlog-build",
      "name": "Build live_source_runs Row",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        640
      ]
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "PASTE_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "live_source_runs",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "wf13-app-runlog",
      "name": "Append live_source_runs",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2220,
        640
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "url": "={{ $json.fetch_url }}",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "domain",
              "value": "={{ $json.screen_name }}"
            },
            {
              "name": "count",
              "value": "={{ $json.max_items }}"
            },
            {
              "name": "v",
              "value": "={{ $json.api_version }}"
            },
            {
              "name": "access_token",
              "value": "BIND_N8N_VK_CREDENTIAL_NEVER_PASTE_TOKEN"
            }
          ]
        },
        "options": {
          "timeout": 15000
        }
      },
      "id": "wf13-mon-http-walls",
      "name": "Fetch VK Group Walls (DISABLED placeholder)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        460,
        980
      ],
      "disabled": true,
      "notes": "STAGED/INERT (DEC-141): DISABLED until Stage C.1 retest + operator approval. Stage 1 of the monitored two-stage flow: wall.get on PUBLIC allowlisted group walls (uses domain/screen_name). access_token is a placeholder \u2014 bind an n8n VK credential. PUBLIC walls only; no private/closed groups, no member extraction."
    },
    {
      "parameters": {
        "url": "={{ $json.fetch_url }}",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "owner_id",
              "value": "={{ $json.owner_id }}"
            },
            {
              "name": "post_id",
              "value": "={{ $json.post_id }}"
            },
            {
              "name": "count",
              "value": "={{ $json.max_items }}"
            },
            {
              "name": "v",
              "value": "={{ $json.api_version }}"
            },
            {
              "name": "access_token",
              "value": "BIND_N8N_VK_CREDENTIAL_NEVER_PASTE_TOKEN"
            }
          ]
        },
        "options": {
          "timeout": 15000
        }
      },
      "id": "wf13-mon-http-comments",
      "name": "Fetch VK Post Comments (DISABLED placeholder)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        680,
        980
      ],
      "disabled": true,
      "notes": "STAGED/INERT (DEC-141): DISABLED until Stage C.1 retest + operator approval. Stage 2 of the monitored two-stage flow: wall.getComments on PUBLIC posts selected by the engine (uses owner_id+post_id). access_token is a placeholder \u2014 bind an n8n VK credential. PUBLIC comments only; no private messages, no hidden contacts."
    },
    {
      "parameters": {
        "jsCode": "// WF13 Monitored VK Engine (Stage C.1, DEC-141) \u2014 two-stage PUBLIC flow: group walls (wall.get) -> RELEVANT\n// posts -> public comments (wall.getComments). PUBLIC DATA ONLY: no private messages, no closed groups, no\n// member/follower extraction, no hidden/inferred contacts, no auto-outreach (CONTACT_AND_OUTREACH_POLICY).\n// STAGED/INERT: the live two-stage HTTP placeholders ship DISABLED -> with no API response this node THROWS.\n// monitored_fixture_mode=true runs a deterministic $0 SIMULATION (no network) exercising post-level + comment-\n// level relevance, bounded post selection (only relevant/competitor posts consume the comment budget), and dedup.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction low(v){return str(v).toLowerCase();}\nconst cfg=$('Set Connector Config').first().json;\nconst caps={\n  maxGroups:Math.min(Number(cfg.monitored_max_groups_per_run)||5,10),\n  maxPosts:Math.min(Number(cfg.monitored_max_posts_per_group)||10,25),\n  maxRel:Math.min(Number(cfg.monitored_max_relevant_posts)||5,15),\n  maxCmtPost:Math.min(Number(cfg.monitored_max_comments_per_post)||20,50),\n  maxTotalCmt:Math.min(Number(cfg.monitored_max_total_comments)||100,300)\n};\n// vocab mirrors Normalize VK Items so engine pre-filter == downstream relevance\nconst STRONG=['\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440','\u0438\u043f\u043e\u0442\u0435\u0447\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440','\u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430','\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f','\u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435','\u0442\u0435\u043d\u0434\u0435\u0440\u043d\u044b\u0435','\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438','\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435','\u0441\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0432\u043a\u0438','\u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435 \u043a\u0440\u0435\u0434\u0438\u0442\u0430','\u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a\u0430','\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043f\u0442\u0441','\u0437\u0430\u043b\u043e\u0433 \u043d\u0435\u0434\u0432\u0438\u0436\u0438\u043c\u043e\u0441\u0442\u0438'];\nconst WEAK=['\u043a\u0440\u0435\u0434\u0438\u0442','\u0437\u0430\u0439\u043c','\u0431\u0430\u043d\u043a','\u043e\u0442\u043a\u0430\u0437','\u0441\u043a\u043e\u0440\u0438\u043d\u0433','\u0441\u0442\u0430\u0432\u043a','\u0437\u0430\u0451\u043c\u0449\u0438\u043a','\u0438\u043f\u043e\u0442\u0435\u043a','\u0444\u0438\u043d\u0430\u043d\u0441','\u0431\u0440\u043e\u043a\u0435\u0440','\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442','\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a','\u0437\u0430\u043b\u043e\u0433'];\nconst HARD_NEG=['\u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0434\u0440\u0435\u0441','\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043e\u043e\u043e','\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438\u043f','\u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440','\u044d\u043a\u0432\u0430\u0439\u0440\u0438\u043d\u0433','pos-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b','\u043a\u0430\u0441\u0441\u0430','\u043f\u0435\u0447\u0430\u0442\u044c \u0438 \u0448\u0442\u0430\u043c\u043f','\u044d\u0446\u043f','\u0430\u0440\u0435\u043d\u0434\u0430 \u043e\u0444\u0438\u0441\u0430','\u043a\u043e\u0432\u043e\u0440\u043a\u0438\u043d\u0433'];\nconst DEMAND=['\u043d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442','\u043d\u0443\u0436\u0435\u043d \u0437\u0430\u0439\u043c','\u043d\u0443\u0436\u043d\u0430 \u0438\u043f\u043e\u0442\u0435\u043a','\u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441','\u043d\u0443\u0436\u043d\u044b \u0434\u0435\u043d\u044c\u0433\u0438','\u0438\u0449\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0430','\u0438\u0449\u0443 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0433\u043e \u0431\u0440\u043e\u043a\u0435\u0440\u0430','\u043f\u043e\u043c\u043e\u0433\u0438\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c','\u043f\u043e\u043c\u043e\u0433\u0438\u0442\u0435 \u0432\u0437\u044f\u0442\u044c','\u0445\u043e\u0447\u0443 \u0432\u0437\u044f\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442','\u0434\u0435\u043d\u044c\u0433\u0438 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433','\u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u043f\u0442\u0441','\u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u043d\u0443\u0436\u043d\u0430 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0430\u044f \u0433\u0430\u0440\u0430\u043d\u0442\u0438','\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437','\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0439 \u043a\u0440\u0435\u0434\u0438\u0442','\u043d\u0443\u0436\u043d\u0430 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f'];\nconst QUESTION=['\u043f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435','\u043f\u043e\u0441\u043e\u0432\u0435\u0442\u0443\u0439\u0442\u0435','\u043a\u0442\u043e \u0431\u0440\u0430\u043b','\u043a\u0442\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u043b\u0441\u044f','\u043a \u043a\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f','\u043a\u0443\u0434\u0430 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f','\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e \u0441\u0442\u043e\u0438\u0442','\u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c','\u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c','\u0433\u0434\u0435 \u0432\u0437\u044f\u0442\u044c','\u0433\u0434\u0435 \u043b\u0443\u0447\u0448\u0435','\u0433\u0434\u0435 \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c','?'];\nconst OBJECTION=['\u043c\u043e\u0448\u0435\u043d\u043d\u0438\u043a','\u043e\u0431\u043c\u0430\u043d','\u043a\u0438\u0434\u0430\u043b','\u043d\u0435 \u0432\u0435\u0440\u044e','\u0440\u0430\u0437\u0432\u043e\u0434','\u0431\u043e\u044e\u0441\u044c','\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442'];\nconst COMPLAINT=['\u043e\u0431\u043c\u0430\u043d\u0443\u043b\u0438','\u043a\u0438\u043d\u0443\u043b\u0438','\u043d\u0435 \u0432\u0435\u0440\u043d\u0443\u043b\u0438','\u0432\u0437\u044f\u043b\u0438 \u0434\u0435\u043d\u044c\u0433\u0438 \u0438','\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u0442\u0435\u043b\u044c\u043d'];\nconst SUPPLIER=['\u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442','\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b','\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u044f\u0432\u043a\u0443','\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u043e \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0443','\u043f\u043e\u043c\u043e\u0433\u0430\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c','\u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a\u0430','\u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435 \u043a\u0440\u0435\u0434\u0438\u0442\u0430','\u043e\u043f\u043b\u0430\u0442\u0430 \u043f\u043e \u0444\u0430\u043a\u0442\u0443','\u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043b\u0438\u0447\u043a\u0443','\u043d\u0430\u0448\u0438 \u0443\u0441\u043b\u0443\u0433\u0438','\u0437\u0432\u043e\u043d\u0438\u0442\u0435 \u043d\u0430\u043c'];\nconst ADMIN=['\u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440','\u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440','\u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0433\u0440\u0443\u043f\u043f\u044b','\u0440\u0435\u043a\u043b\u0430\u043c\u0430 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u0430','\u0437\u0430\u043a\u0440\u0435\u043f\u043b\u0451\u043d','\u0437\u0430\u043a\u0440\u0435\u043f\u043b\u0435\u043d'];\nconst SPAM=['\u043a\u0430\u0437\u0438\u043d\u043e','\u0441\u0442\u0430\u0432\u043a\u0438 \u043d\u0430 \u0441\u043f\u043e\u0440\u0442','\u0440\u043e\u0437\u044b\u0433\u0440\u044b\u0448','crypto','\u043a\u0440\u0438\u043f\u0442','\u0437\u0430\u0440\u0430\u0431\u043e\u0442\u043e\u043a \u0431\u0435\u0437 \u0432\u043b\u043e\u0436\u0435\u043d\u0438\u0439','18+'];\nfunction any(b,a){return a.some(t=>b.indexOf(t)>=0);}\nfunction cnt(b,a){return a.filter(t=>b.indexOf(t)>=0).length;}\n// post-level relevance decides whether comments are fetched at all\nfunction classifyPost(text){\n  const b=low(text), neg=any(b,HARD_NEG), strong=cnt(b,STRONG), weak=cnt(b,WEAK);\n  const looksAd=/\u0442\u0435\u043b[:.]|\\+7|\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b|\u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442|\u043f\u043e\u0434 \u043a\u043b\u044e\u0447|\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u044f\u0432\u043a\u0443/i.test(text);\n  if(neg&&!strong)return 'hard_negative';\n  if(strong&&looksAd)return 'competitor_activity';\n  if(any(b,DEMAND)||any(b,QUESTION))return 'relevant_demand_context';\n  if(strong||weak>=2)return 'market_signal';\n  return 'irrelevant';\n}\nfunction postSelectable(cls){return cls==='relevant_demand_context'||cls==='competitor_activity';}\n// comment-level relevance: a comment must itself show demand/question/objection/complaint to be a lead\nfunction classifyComment(text,role){\n  const b=low(text); role=low(role);\n  if(role==='admin'||role==='moderator'||any(b,ADMIN))return 'admin';\n  if(any(b,SPAM))return 'spam';\n  const sup=cnt(b,SUPPLIER), dem=any(b,DEMAND), q=any(b,QUESTION), obj=any(b,OBJECTION), comp=any(b,COMPLAINT);\n  if(role==='broker'||role==='supplier'||(sup>=2&&!dem&&!q&&!obj&&!comp))return 'supplier';\n  // a comment must itself show finance relevance (not just a bare '?'): demand, OR question/objection/complaint WITH niche evidence\n  const niche=any(b,STRONG)||any(b,WEAK);\n  if(dem||((q||obj||comp)&&niche))return 'lead';\n  return 'irrelevant';\n}\nfunction builtinDataset(){return [\n { screen_name:'kredity_msk_fixture', category:'competitor', enabled:true, region:'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e', posts:[\n   { post_id:'901', sim_status:'ok', text:'\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440: \u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432, \u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b. \u0417\u0432\u043e\u043d\u0438\u0442\u0435 +7 000 000-00-09', comments:[\n     { comment_id:'9011', author_handle:'id_user_mon_1', author_name:'\u041e\u043b\u0435\u0433 (fixture)', author_role:'user', author_profile_public:true, text:'\u0421\u0440\u043e\u0447\u043d\u043e \u043d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u0430 \u0434\u0432\u0443\u0445 \u0431\u0430\u043d\u043a\u043e\u0432, \u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f, \u043a\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043c\u043e\u0433?' },\n     { comment_id:'9012', author_handle:'id_broker_mon', author_name:'\u0411\u0440\u043e\u043a\u0435\u0440 (fixture)', author_role:'broker', author_profile_public:true, text:'\u041f\u043e\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c! \u041e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u044f\u0432\u043a\u0443, \u0437\u0432\u043e\u043d\u0438\u0442\u0435 \u043d\u0430\u043c' },\n     { comment_id:'9013', author_handle:'id_admin_mon', author_name:'\u0410\u0434\u043c\u0438\u043d (fixture)', author_role:'admin', author_profile_public:true, text:'\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440: \u0440\u0435\u043a\u043b\u0430\u043c\u0430 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u0430, \u0447\u0438\u0442\u0430\u0439\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0433\u0440\u0443\u043f\u043f\u044b' } ] },\n   { post_id:'902', sim_status:'comments_disabled', text:'\u041f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435, \u0433\u0434\u0435 \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u0430?', comments:[] },\n   { post_id:'901', sim_status:'ok', _dup:true, text:'\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440: \u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432, \u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b. \u0417\u0432\u043e\u043d\u0438\u0442\u0435 +7 000 000-00-09', comments:[] } ] },\n { screen_name:'biznes_finansy_fixture', category:'finance_community', enabled:true, region:'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e', posts:[\n   { post_id:'903', sim_status:'ok', text:'\u041e\u0431\u0437\u043e\u0440 \u0440\u044b\u043d\u043a\u0430 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0432\u0430\u043d\u0438\u044f: \u0431\u0430\u043d\u043a\u0438 \u0443\u0436\u0435\u0441\u0442\u043e\u0447\u0438\u043b\u0438 \u0441\u043a\u043e\u0440\u0438\u043d\u0433, \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u0440\u0430\u0441\u0442\u0451\u0442', comments:[] },\n   { post_id:'904', sim_status:'ok', text:'\u041f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435, \u0433\u0434\u0435 \u0432\u0437\u044f\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430?', comments:[\n     { comment_id:'9041', author_handle:'id_user_mon_5', author_name:'\u0418\u041f (fixture)', author_role:'user', author_profile_public:true, text:'\u041d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 (\u041e\u041e\u041e), \u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430, \u0433\u0434\u0435 \u043b\u0443\u0447\u0448\u0435 \u0432\u0437\u044f\u0442\u044c?' },\n     { comment_id:'9041', author_handle:'id_user_mon_5', author_name:'\u0418\u041f (fixture)', author_role:'user', author_profile_public:true, _dup:true, text:'\u041d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 (\u041e\u041e\u041e), \u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430, \u0433\u0434\u0435 \u043b\u0443\u0447\u0448\u0435 \u0432\u0437\u044f\u0442\u044c?' },\n     { comment_id:'9042', author_handle:'id_user_mon_6', author_name:'\u0427\u0438\u0442 (fixture)', author_role:'user', author_profile_public:false, text:'\u041f\u0440\u043e\u0441\u0442\u043e \u0447\u0438\u0442\u0430\u044e, \u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u043f\u043e\u0441\u0442' } ] } ] },\n { screen_name:'uslugi_msk_fixture', category:'city_community', enabled:true, region:'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e', posts:[\n   { post_id:'905', sim_status:'ok', text:'\u042e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u041e\u041e\u041e, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u041e\u041e\u041e \u043f\u043e\u0434 \u043a\u043b\u044e\u0447, \u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440\u0441\u043a\u043e\u0435 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u0435', comments:[] },\n   { post_id:'906', sim_status:'ok', text:'\u041a\u0443\u0434\u0430 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0437\u0430 \u0434\u0435\u043d\u044c\u0433\u0430\u043c\u0438 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u041f\u0422\u0421?', comments:[\n     { comment_id:'9061', author_handle:'id_user_mon_7', author_name:'\u041c\u0430\u0440\u0438\u043d\u0430 (fixture)', author_role:'user', author_profile_public:true, has_contact:true, contact_provable:true, text:'\u041d\u0443\u0436\u043d\u044b \u0434\u0435\u043d\u044c\u0433\u0438 \u043f\u043e\u0434 \u0437\u0430\u043b\u043e\u0433 \u041f\u0422\u0421 \u0441\u0440\u043e\u0447\u043d\u043e, \u0438\u0449\u0443 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0432 \u041c\u043e\u0441\u043a\u0432\u0435. \u0422\u0435\u043b\u0435\u0444\u043e\u043d +7 000 000-00-01' },\n     { comment_id:'9062', author_handle:'id_user_mon_8', author_name:'\u0418\u0433\u043e\u0440\u044c (fixture)', author_role:'user', author_profile_public:true, text:'\u0414\u043b\u044f \u0418\u041f \u043d\u0443\u0436\u0435\u043d \u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0439 \u043a\u0440\u0435\u0434\u0438\u0442, \u043f\u043e\u0441\u043e\u0432\u0435\u0442\u0443\u0439\u0442\u0435 \u0431\u0430\u043d\u043a' },\n     { comment_id:'9063', author_handle:'id_user_mon_9', author_name:'\u0410\u043d\u043e\u043d\u0438\u043c (fixture)', author_role:'user', author_profile_public:false, has_contact:true, contact_provable:false, text:'\u041d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442, \u0437\u0432\u043e\u043d\u0438\u0442\u0435 +7 000 000-00-77, \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u0442\u044c \u043d\u0435 \u043c\u043e\u0433\u0443' } ] } ] },\n { screen_name:'predprinimateli_fixture', category:'entrepreneur_community', enabled:true, region:'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e', posts:[\n   { post_id:'907', sim_status:'ok', text:'\u041e\u0431\u0441\u0443\u0436\u0434\u0430\u0435\u043c: \u043d\u0443\u0436\u043d\u0430 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0430\u044f \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f \u043f\u043e\u0434 \u0442\u0435\u043d\u0434\u0435\u0440, \u043f\u043e\u0441\u043e\u0432\u0435\u0442\u0443\u0439\u0442\u0435', comments:[\n     { comment_id:'9071', author_handle:'id_user_mon_10', author_name:'\u0414\u0438\u0440 (fixture)', author_role:'user', author_profile_public:true, text:'\u041d\u0443\u0436\u043d\u0430 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0430\u044f \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f \u043f\u043e\u0434 \u0442\u0435\u043d\u0434\u0435\u0440 \u0434\u043b\u044f \u041e\u041e\u041e, \u0433\u0434\u0435 \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c?' } ] },\n   { post_id:'908', sim_status:'deleted', text:'', comments:[] },\n   { post_id:'909', sim_status:'error', text:'', comments:[] },\n   { post_id:'910', sim_status:'rate_limited', text:'', comments:[] },\n   { post_id:'911', sim_status:'ok', text:'\u041f\u043e\u0434\u0441\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0440\u043e \u043a\u0440\u0435\u0434\u0438\u0442\u044b \u0438 \u0437\u0430\u0439\u043c\u044b, \u043a\u0443\u0434\u0430 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f?', comments:[\n     { comment_id:'9111', author_handle:'id_user_mon_11', author_name:'\u0412\u0430\u0441\u044f (fixture)', author_role:'user', author_profile_public:true, text:'\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u043a\u0430\u043a \u0434\u0435\u043b\u0430? \u041d\u0435 \u043f\u043e \u0442\u0435\u043c\u0435' },\n     { comment_id:'9112', author_handle:'id_spam_mon', author_name:'\u0421\u043f\u0430\u043c (fixture)', author_role:'user', author_profile_public:true, text:'\u041a\u0430\u0437\u0438\u043d\u043e \u043e\u043d\u043b\u0430\u0439\u043d, \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u043e\u043a \u0431\u0435\u0437 \u0432\u043b\u043e\u0436\u0435\u043d\u0438\u0439, 18+' } ] },\n   { post_id:'912', sim_status:'ok', text:'\u0421 \u0434\u043d\u0451\u043c \u0433\u043e\u0440\u043e\u0434\u0430, \u0434\u043e\u0440\u043e\u0433\u0438\u0435 \u0434\u0440\u0443\u0437\u044c\u044f, \u0432\u0441\u0435\u0445 \u043f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u0435\u043c!', comments:[\n     { comment_id:'9121', author_handle:'id_user_x', author_name:'X (fixture)', author_role:'user', author_profile_public:true, text:'\u041d\u0443\u0436\u0435\u043d \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u0430, \u043f\u043e\u043c\u043e\u0433\u0438\u0442\u0435!' } ] } ] }\n];}\nfunction monitoredSimulate(groups){\n  const PUB='2026-06-15T10:00:00+03:00';\n  const st={groups_requested:0,groups_processed:0,group_errors:0,posts_received:0,structurally_valid_posts:0,\n    business_relevant_posts:0,hard_skipped_posts:0,market_signal_posts:0,posts_selected_for_comments:0,\n    comments_disabled_posts:0,deleted_posts:0,unavailable_posts:0,api_errors:0,rate_limit_events:0,retries:0,\n    comments_received:0,structurally_valid_comments:0,business_relevant_comments:0,supplier_comments_skipped:0,\n    admin_comments_skipped:0,spam_comments_skipped:0,irrelevant_comments_skipped:0,duplicate_posts:0,\n    duplicate_comments:0,contacts_found:0,contacts_without_evidence:0};\n  const items=[]; const seenPost=new Set(), seenCmt=new Set(); let totalCmt=0;\n  st.groups_requested=groups.length;\n  const enabled=groups.filter(g=>g.enabled!==false).slice(0,caps.maxGroups);\n  for(const g of enabled){\n    if(low(g.sim_status)==='group_error'){st.group_errors++;continue;}\n    let selectedInGroup=0;\n    const posts=(g.posts||[]).slice(0,caps.maxPosts);\n    for(const p of posts){\n      st.posts_received++;\n      const sim=low(p.sim_status)||'ok';\n      if(sim==='error'){st.api_errors++;continue;}\n      if(sim==='rate_limited'){st.rate_limit_events++;continue;}\n      if(sim==='deleted'){st.deleted_posts++;continue;}\n      if(sim==='unavailable'){st.unavailable_posts++;continue;}\n      if(!str(p.text))continue;\n      st.structurally_valid_posts++;\n      const pkey=low(g.screen_name)+'_p_'+str(p.post_id);\n      if(p._dup||seenPost.has(pkey)){st.duplicate_posts++;continue;}\n      seenPost.add(pkey);\n      const cls=classifyPost(p.text);\n      if(cls==='hard_negative'){st.hard_skipped_posts++;continue;}\n      if(cls==='relevant_demand_context'||cls==='competitor_activity'||cls==='market_signal')st.business_relevant_posts++;\n      const purl='https://vk.com/wall-777_'+str(p.post_id);\n      if(cls==='market_signal'){st.market_signal_posts++;\n        items.push({json:{group:g.screen_name,group_title:g.screen_name,item_type:'post',item_id:str(p.post_id),comment_id:'',item_url:purl,author_handle:'id_editor_'+str(p.post_id),author_name:'',author_profile_public:false,published_at:PUB,text:p.text,_monitored_category:g.category}});\n        continue;}\n      if(cls==='competitor_activity'){\n        items.push({json:{group:g.screen_name,group_title:g.screen_name,item_type:'post',item_id:str(p.post_id),comment_id:'',item_url:purl,author_handle:'id_competitor_'+str(p.post_id),author_name:'Competitor (fixture)',author_profile_public:true,published_at:PUB,text:p.text,_monitored_category:g.category}});}\n      if(!postSelectable(cls))continue;\n      if(selectedInGroup>=caps.maxRel)continue;\n      selectedInGroup++; st.posts_selected_for_comments++;\n      if(sim==='comments_disabled'){st.comments_disabled_posts++;continue;}\n      let cmtThisPost=0;\n      for(const c of (p.comments||[])){\n        if(totalCmt>=caps.maxTotalCmt)break;\n        if(cmtThisPost>=caps.maxCmtPost)break;\n        st.comments_received++; totalCmt++; cmtThisPost++;\n        if(!str(c.text))continue;\n        const ckey=low(g.screen_name)+'_c_'+str(p.post_id)+'_'+str(c.comment_id);\n        if(c._dup||seenCmt.has(ckey)){st.duplicate_comments++;continue;}\n        seenCmt.add(ckey);\n        st.structurally_valid_comments++;\n        const cc=classifyComment(c.text,c.author_role);\n        if(cc==='admin'){st.admin_comments_skipped++;continue;}\n        if(cc==='spam'){st.spam_comments_skipped++;continue;}\n        if(cc==='supplier'){st.supplier_comments_skipped++;continue;}\n        if(cc==='irrelevant'){st.irrelevant_comments_skipped++;continue;}\n        st.business_relevant_comments++;\n        let text=str(c.text);\n        if(c.has_contact){ if(c.contact_provable===false){ st.contacts_without_evidence++; text=text.replace(/(?:\\+7|8)[\\s\\-()]*\\d{3}[\\s\\-()]*\\d{3}[\\s\\-]*\\d{2}[\\s\\-]*\\d{2}/g,'[contact omitted: no public source url]'); } else { st.contacts_found++; } }\n        items.push({json:{group:g.screen_name,group_title:g.screen_name,item_type:'comment',item_id:str(p.post_id),comment_id:str(c.comment_id),item_url:'https://vk.com/wall-777_'+str(p.post_id)+'?reply='+str(c.comment_id),author_handle:str(c.author_handle),author_name:str(c.author_name),author_profile_public:(c.author_profile_public===true),published_at:PUB,text:text,_monitored_category:g.category}});\n      }\n    }\n    st.groups_processed++;\n  }\n  st.unique_count=items.length;\n  st.duplicate_count=st.duplicate_posts+st.duplicate_comments;\n  st.hard_skipped_comments=st.admin_comments_skipped+st.spam_comments_skipped;\n  return {items:items, stats:st};\n}\nconst fixtureMon=(cfg.monitored_fixture_mode===true||low(cfg.monitored_fixture_mode)==='true');\nif(fixtureMon){\n  const ds=(Array.isArray(cfg.monitored_fixture_dataset)&&cfg.monitored_fixture_dataset.length)?cfg.monitored_fixture_dataset:builtinDataset();\n  const out=monitoredSimulate(ds);\n  out.stats.run_mode='monitored_fixture_sim'; out.stats.source_cost_usd=0;\n  const sd=$getWorkflowStaticData('global'); sd.wf13_monitored=out.stats;\n  if(!out.items.length)return [{ json: Object.assign({_monitored_no_items:true}, out.stats) }];\n  return out.items;\n}\n// LIVE monitored: INERT \u2014 the two-stage HTTP placeholders (wall.get / wall.getComments) are DISABLED.\nconst inputs=$input.all().map(i=>i.json);\nconst anyResp=inputs.some(j=>j&&(j.response||(j.body&&j.body.response)));\nif(!anyResp){ throw new Error('WF13 monitored engine: no VK API response \u2014 the two-stage HTTP placeholders (wall.get / wall.getComments) are DISABLED (inert by design, DEC-141). Live monitored crawling is STAGED/BLOCKED until Stage C.1 retest + operator VK credential + explicit approval. Use monitored_fixture_mode=true for the deterministic $0 simulation. See docs/VK_MONITORED_SOURCE_RUNBOOK.md.'); }\nthrow new Error('WF13 monitored live two-stage parsing is staged/disabled \u2014 enable per docs/VK_MONITORED_SOURCE_RUNBOOK.md only after Stage C.1 retest and explicit operator approval.');"
      },
      "id": "wf13-monitored-engine",
      "name": "Monitored VK Engine (inert + fixture sim)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        980
      ],
      "notes": "Monitored two-stage engine (DEC-141): post-level + comment-level relevance, bounded post selection, dedup, counters. INERT in live (HTTP placeholders disabled -> throws). monitored_fixture_mode=true = deterministic $0 simulation."
    }
  ],
  "connections": {
    "Manual Start": {
      "main": [
        [
          {
            "node": "Set Connector Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Connector Config": {
      "main": [
        [
          {
            "node": "IF fixture_mode?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF fixture_mode?": {
      "main": [
        [
          {
            "node": "Build Fixture VK Group Items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "LIVE VK Approval Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Fixture VK Group Items": {
      "main": [
        [
          {
            "node": "Normalize VK Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize VK Items": {
      "main": [
        [
          {
            "node": "Read market_record_registry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read market_record_registry": {
      "main": [
        [
          {
            "node": "Deduplicate Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deduplicate Items": {
      "main": [
        [
          {
            "node": "Build raw_market_records Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build raw_market_records Rows": {
      "main": [
        [
          {
            "node": "Append raw_market_records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append raw_market_records": {
      "main": [
        [
          {
            "node": "Build market_record_registry Rows",
            "type": "main",
            "index": 0
          },
          {
            "node": "Build agent_requests Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build market_record_registry Rows": {
      "main": [
        [
          {
            "node": "Append market_record_registry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build agent_requests Row": {
      "main": [
        [
          {
            "node": "Append agent_requests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append agent_requests": {
      "main": [
        [
          {
            "node": "Build live_source_runs Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LIVE VK Approval Gate": {
      "main": [
        [
          {
            "node": "Fetch VK Public Wall (DISABLED placeholder)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch VK Public Wall (DISABLED placeholder)": {
      "main": [
        [
          {
            "node": "Parse Live VK Items (inert)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Live VK Items (inert)": {
      "main": [
        [
          {
            "node": "Normalize VK Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build live_source_runs Row": {
      "main": [
        [
          {
            "node": "Append live_source_runs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append live_source_runs": {
      "main": [
        [
          {
            "node": "Final Summary Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch VK Group Walls (DISABLED placeholder)": {
      "main": [
        [
          {
            "node": "Monitored VK Engine (inert + fixture sim)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch VK Post Comments (DISABLED placeholder)": {
      "main": [
        [
          {
            "node": "Monitored VK Engine (inert + fixture sim)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monitored VK Engine (inert + fixture sim)": {
      "main": [
        [
          {
            "node": "Normalize VK Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "wf13-v003-vk-lead-source-wallcomments-stage35-20260617"
}