{
  "name": "09 - Avito Classifieds Listing Connector",
  "nodes": [
    {
      "id": "a9000000-0000-0000-0000-0000000000n1",
      "name": "Overview Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -480,
        -260
      ],
      "parameters": {
        "content": "## 09 \u2014 Avito/Classifieds Listing Connector \u2014 Stage 3.3\n\n\u041f\u0415\u0420\u0412\u042b\u0419 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u043e\u0441\u043b\u0435 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0451\u043c\u0430 (Workflow 07). \u041d\u0415 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u0438 \u041d\u0415 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440.\n\n\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442:\n1. \u0420\u0443\u0447\u043d\u043e\u0439 \u0441\u0442\u0430\u0440\u0442 \u2192 Set Avito Connector Config (fixture_mode=true \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e).\n2. IF fixture_mode=true \u2192 Build Fixture Avito Listings (\u0431\u0435\u0437 Apify, $0). \u0418\u043d\u0430\u0447\u0435 \u2192 Apify Avito/Classifieds Actor Request (live, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 fixture_mode=false).\n3. Normalize Avito Listings \u2192 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432 shape raw_market_records (\u0431\u0435\u0437 LLM): record_type_hint, touchpoint_type, semantic_keywords, ad_channel_hint, competitor_name, dedup_key, text_hash, \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442/entity.\n4. Read market_record_registry \u2192 Deduplicate Listings (\u043f\u043e dedup_key).\n5. Build raw_market_records Rows (40 \u043a\u043e\u043b\u043e\u043d\u043e\u043a) \u2014 \u0432\u0441\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 (\u0430\u0443\u0434\u0438\u0442) \u043f\u0440\u0438 write_duplicate_audit=true.\n6. Build market_record_registry Rows (15 \u043a\u043e\u043b\u043e\u043d\u043e\u043a) \u2014 \u0422\u041e\u041b\u042c\u041a\u041e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435.\n7. Build agent_requests Row (21 \u043a\u043e\u043b\u043e\u043d\u043a\u0430, status=completed).\n8. Append: agent_requests, raw_market_records, market_record_registry (\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435).\n9. Final Summary.\n\n\u041a\u041e\u041c\u041f\u0415\u0422\u0415\u041d\u0422\u041d\u041e\u0421\u0422\u042c: Competitor Ad Intelligence / Semantic Intelligence \u2014 \u043e\u0444\u0444\u0435\u0440/\u0446\u0435\u043d\u0430/\u0443\u0441\u043b\u043e\u0432\u0438\u044f/\u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430/\u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u0430 \u0438\u0437 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0439.\n\n\u0427\u0415\u0413\u041e \u041d\u0415 \u0414\u0415\u041b\u0410\u0415\u0422:\n- \u041d\u0415 \u043f\u0438\u0448\u0435\u0442 \u0432 \u0431\u0438\u0437\u043d\u0435\u0441-\u0432\u043a\u043b\u0430\u0434\u043a\u0438 (results/review_queue/monitor_queue/content_queue/skipped_log/technical_errors) \u2014 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 Workflow 08.\n- \u041d\u0415 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 Apify \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e (fixture_mode=true), \u041d\u0415 Firecrawl, \u041d\u0415 Claude.\n- \u041d\u0415 \u0430\u0432\u0442\u043e-\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 Workflow 08. \u0425\u044d\u043d\u0434\u043e\u0444\u0444 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e.\n- \u041d\u0415 Telegram/Instagram/VK/Dzen \u043f\u0430\u0440\u0441\u0435\u0440, \u041d\u0415 Telegram-\u0431\u043e\u0442, \u041d\u0415 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u0435. active=false.\n\n\u0424\u0418\u041b\u042c\u0422\u0420 \u0411\u0418\u0417\u041d\u0415\u0421-\u0420\u0415\u041b\u0415\u0412\u0410\u041d\u0422\u041d\u041e\u0421\u0422\u0418 (DEC-095): \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0422\u041e\u041b\u042c\u041a\u041e \u043f\u043e evidence \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f (title + description + \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 slug URL + category). \u041f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u041d\u0418\u041a\u041e\u0413\u0414\u0410 \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u043c. Live: hard-negative (\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 \u041e\u041e\u041e / \u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440\u0438\u044f / \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 / \u042d\u0426\u041f / \u0430\u0440\u0435\u043d\u0434\u0430 \u043e\u0444\u0438\u0441\u0430\u2026) \u0431\u0435\u0437 \u0441\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0433\u043e evidence \u2192 hard_skipped \u2014 \u041d\u0415 \u043f\u0438\u0448\u0435\u0442\u0441\u044f \u0432 raw_market_records \u0438 market_record_registry \u0438 \u041d\u0415 \u0440\u0430\u0441\u0445\u043e\u0434\u0443\u0435\u0442 pipeline_limit. pipeline_limit=10 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043a \u043f\u0440\u0438\u043d\u044f\u0442\u044b\u043c \u0431\u0438\u0437\u043d\u0435\u0441-\u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u043c \u0437\u0430\u043f\u0438\u0441\u044f\u043c. Fixture-\u043f\u0443\u0442\u044c \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0451\u043d (6 \u0437\u0430\u043f\u0438\u0441\u0435\u0439, POS-\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u2192 skipped_log).",
        "height": 720,
        "width": 600,
        "color": 4
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000n2",
      "name": "Setup and Test Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        160,
        -260
      ],
      "parameters": {
        "content": "## \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438 \u0442\u0435\u0441\u0442\n\n1. \u041d\u0415 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c (active=false).\n2. \u0412\u043a\u043b\u0430\u0434\u043a\u0438 \u0443\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u044b (Stage 3.1): agent_requests (21), raw_market_records (40), market_record_registry (15).\n3. \u041f\u041e\u0421\u041b\u0415 \u0418\u041c\u041f\u041e\u0420\u0422\u0410 \u043f\u0435\u0440\u0435\u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u043a\u0440\u0435\u0434\u0435\u043d\u0448\u043b Google Sheets \u043d\u0430 4 \u043d\u043e\u0434\u0430\u0445 (Read market_record_registry, Append agent_requests, Append raw_market_records, Append market_record_registry) \u2192 Google Sheets - Marketing Scout Service Account.\n4. \u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c PASTE_SPREADSHEET_ID_HERE \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 Spreadsheet ID \u043d\u0430 \u0432\u0441\u0435\u0445 Google Sheets \u043d\u043e\u0434\u0430\u0445.\n5. Apify \u043d\u043e\u0434\u0430 \u041d\u0415 \u043d\u0443\u0436\u043d\u0430 \u0434\u043b\u044f fixture-\u0442\u0435\u0441\u0442\u0430 \u0438 \u041d\u0415 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438 fixture_mode=true. \u0414\u043b\u044f live: \u0437\u0430\u0434\u0430\u0442\u044c apify_actor_id, \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c Apify \u043a\u0440\u0435\u0434\u0435\u043d\u0448\u043b (\u0438\u043b\u0438 \u0442\u043e\u043a\u0435\u043d), \u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c fixture_mode=false + live_mode=true.\n\n\u0422\u0435\u0441\u0442 1 (fixture, \u043f\u0443\u0441\u0442\u043e\u0439 \u0440\u0435\u0435\u0441\u0442\u0440):\n- fixture_mode=true. Execute once.\n- \u041e\u0436\u0438\u0434\u0430\u0435\u043c\u043e: 6 \u0441\u0442\u0440\u043e\u043a \u0432 raw_market_records; 6 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0432 market_record_registry; 1 agent_requests (status=completed); 5 competitor (monitor_queue \u043f\u0440\u043e\u0433\u043d\u043e\u0437) + 1 irrelevant (skipped_log \u043f\u0440\u043e\u0433\u043d\u043e\u0437).\n\n\u0422\u0435\u0441\u0442 2 (fixture, \u043f\u043e\u0432\u0442\u043e\u0440):\n- Execute \u0441\u043d\u043e\u0432\u0430 \u2192 \u0432\u0441\u0435 6 dedup_status=duplicate_in_registry, approval_status=duplicate; raw +6 (\u0430\u0443\u0434\u0438\u0442 \u043f\u0440\u0438 write_duplicate_audit=true); \u0440\u0435\u0435\u0441\u0442\u0440 +0.\n\n\u0422\u0435\u0441\u0442 3 (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e, live Apify, max_items=5):\n- fixture_mode=false, live_mode=true, apify_actor_id \u0437\u0430\u0434\u0430\u043d. \u0417\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c Apify. 0 Firecrawl/Claude.\n\n\u0422\u0435\u0441\u0442 4 (\u0445\u044d\u043d\u0434\u043e\u0444\u0444):\n- \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Workflow 08 \u0412\u0420\u0423\u0427\u041d\u0423\u042e \u043d\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0445 raw_market_records. \u0410\u0432\u0442\u043e-\u0445\u044d\u043d\u0434\u043e\u0444\u0444\u0430 \u043d\u0435\u0442.\n\n\u0421\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c: fixture = $0. live = \u0442\u043e\u043b\u044c\u043a\u043e Apify actor. \u0410\u043d\u0430\u043b\u0438\u0437\u0430 \u0437\u0434\u0435\u0441\u044c \u043d\u0435\u0442 ($0).\n\nLIVE SMOKE (DEC-095): actor_limit=10 (\u0432 Apify), pipeline_limit=10 (\u0431\u0438\u0437\u043d\u0435\u0441-\u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438). Hard-negative live false positives (\u044e\u0440. \u0430\u0434\u0440\u0435\u0441, POS-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0438 \u0442.\u043f.) \u0441\u0447\u0438\u0442\u0430\u044e\u0442\u0441\u044f \u0432 hard_skipped_items \u0438 \u043d\u0435 \u043f\u0438\u0448\u0443\u0442\u0441\u044f. result_summary: actor_items_received / structurally_valid_items / invalid_items / business_relevant_items / hard_skipped_items / unique / duplicates / over_pipeline_limit.",
        "height": 720,
        "width": 600,
        "color": 4
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000ap",
      "name": "Apify Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        800,
        -260
      ],
      "parameters": {
        "content": "## Apify Avito/Classifieds Actor Request (live only)\n\n\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0422\u041e\u041b\u042c\u041a\u041e \u043a\u043e\u0433\u0434\u0430 fixture_mode=false (\u0432\u0435\u0442\u043a\u0430 ELSE \u0438\u0437 IF). \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u041d\u0415 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.\n\n- URL: https://api.apify.com/v2/acts/PASTE_AVITO_ACTOR_ID/run-sync-get-dataset-items\n- \u041c\u0435\u0442\u043e\u0434: POST, \u0442\u0435\u043b\u043e JSON \u0441\u043e search_queries + maxItems \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.\n- \u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f: \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c Apify \u043a\u0440\u0435\u0434\u0435\u043d\u0448\u043b (HTTP Header Auth) \u0418\u041b\u0418 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0432 query (?token=...). \u0412 \u0444\u0430\u0439\u043b\u0435 \u041d\u0415\u0422 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430/\u043a\u043b\u044e\u0447\u0430.\n- onError=continueRegularOutput: \u043f\u0440\u0438 \u0441\u0431\u043e\u0435 actor \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u043f\u0443\u0441\u0442\u043e, \u043f\u0440\u043e\u0433\u043e\u043d \u043d\u0435 \u0443\u043f\u0430\u0434\u0451\u0442.\n\n\u0411\u0415\u0417\u041e\u041f\u0410\u0421\u041d\u041e\u0421\u0422\u042c: \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c live \u0431\u0435\u0437 \u044f\u0432\u043d\u043e\u0433\u043e \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e actor. \u041d\u0435 \u0441\u043a\u0440\u0435\u0439\u043f\u0438\u0442\u044c Avito \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e. \u0421\u043e\u0431\u043b\u044e\u0434\u0430\u0442\u044c ToS \u043f\u043b\u043e\u0449\u0430\u0434\u043a\u0438.",
        "height": 320,
        "width": 440,
        "color": 5
      }
    },
    {
      "id": "a9000000-0000-0000-0000-000000000002",
      "name": "Manual Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        120,
        520
      ],
      "parameters": {}
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000cf",
      "name": "Set Avito Connector Config",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        340,
        520
      ],
      "parameters": {
        "jsCode": "// Avito/Classifieds connector config. Deterministic ids; MSK timestamps; no external calls.\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();\n// --- S3-D24 multi-query planner: declared search_queries MUST drive actor discovery, not a single URL.\nconst SEARCH_QUERIES=['\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440 \u041c\u043e\u0441\u043a\u0432\u0430','\u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430 \u041c\u043e\u0441\u043a\u0432\u0430','\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u041c\u043e\u0441\u043a\u0432\u0430','\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u041c\u043e\u0441\u043a\u0432\u0430','\u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u043c \u0418\u041f'];\nconst REGION='\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e';\nconst ACTOR_LIMIT=10; // overall batch limit across ALL queries\nfunction _slug(r){r=String(r||'').toLowerCase();return (r.indexOf('\u043c\u043e\u0441\u043a\u0432\u0430')>=0)?'moskva':'moskva';}\nconst _overall=ACTOR_LIMIT;\nconst _n=Math.max(1,Math.min(SEARCH_QUERIES.length,_overall)); // never plan more queries than the overall cap\nconst _base=Math.floor(_overall/_n);let _rem=_overall-_base*_n;\nconst _plan=SEARCH_QUERIES.slice(0,_n).map(function(q,i){var a=_base+(_rem>0?1:0);if(_rem>0)_rem--;return {query_index:i+1,search_query:q,source_search_url:'https://www.avito.ru/'+_slug(REGION)+'/predlozheniya_uslug?q='+encodeURIComponent(q),allocation:Math.max(1,a)};});\nconst _startUrls=_plan.map(function(p){return p.source_search_url;});\n// Patch 5: actor_start_urls (url+userData) is kept ONLY for internal mapping/tests. The live Apify actor\n// (fatihtahta~avito-russia-scraper) receives plain string start_urls; per-record query origin is recovered in\n// Normalize from the actor parentSourceUrl matched against query_plan (NOT from these userData objects).\nconst _actorStartUrls=_plan.map(function(p){return {url:p.source_search_url,userData:{search_query:p.search_query,source_search_url:p.source_search_url,query_index:p.query_index}};});\nvar base = ({\n  fixture_mode:true,\n  live_mode:false,                 // \u00a78.1 live Apify requires fixture_mode=false AND live_mode=true AND approval token AND budget\n  data_mode:'fixture',             // fixture | live | manual_test \u2014 set with the mode you actually run (\u00a713)\n  require_approval_token:true,     // \u00a78.1 paid live path is BLOCKED unless approval_token === expected_approval_token\n  approval_token:'',               // operator pastes the one-time token here ONLY for an approved live run (value never logged)\n  expected_approval_token:'AVITO_LIVE_APPROVED',  // guard compares against this; logs approval_token_used=yes/no only\n  max_budget_usd:0.50,             // \u00a78.1 hard cost guard for a live run\n  source_type:'classified',\n  platform:'avito',\n  requested_by:'operator',\n  request_type:'classified_competitor_discovery',\n  region:REGION,\n  service_focus:'credit_broker',\n  search_queries:SEARCH_QUERIES,\n  start_urls:_startUrls,\n  actor_start_urls:_actorStartUrls,\n  query_plan:_plan,\n  query_count:_plan.length,\n  overall_max_items:_overall,\n  per_query_allocation:Math.max(1,_base),\n  max_items:6,\n  actor_limit:_overall,\n  pipeline_limit:10,\n  live_max_items:10,\n  include_irrelevant_control_fixture:true,\n  approval_status_for_unique:'new',\n  write_duplicate_audit:true,\n  hard_skip_debug_audit:false,\n  duplicate_next_action:'monitor_duplicate',\n  apify_actor_id:'fatihtahta~avito-russia-scraper',\n  proxy_config:{useApifyProxy:true,apifyProxyGroups:['RESIDENTIAL']}, // AVITO-PROXY-001: Avito anti-bot -> residential proxy\n  detail_enrichment_enabled:false, // \u00a78.4 detail transport is STAGED/DISABLED; degraded discovery cards are preserved, not invented\n  detail_transport:'apify_detail_actor', // documented transport name; no live call without approval\n  apify_token_placeholder:'PASTE_APIFY_TOKEN_OR_USE_CREDENTIAL',\n  spreadsheet_id_placeholder:'PASTE_SPREADSHEET_ID',\n  agent_request_id:'avito_req_'+stamp,\n  run_id:'avito_'+stamp,\n  created_at:now\n});\n\n// --- Stage 5 agent-called mode: WF20 passes the APPROVED plan bounds. Live runs still pass ONLY through the\n// --- same LIVE Apify Safety Gate (fixture stays the default when data_mode!=='live').\nvar __ci=null;try{var __c=$('When Called by Agent').first().json;if(__c&&String(__c.agent_request_id||'').length)__ci=__c;}catch(e){__ci=null;}\nif(__ci){\n  var __qs=String(__ci.search_queries||'').split(/[;|]/).map(function(s){return s.trim();}).filter(Boolean).slice(0,3);\n  if(!__qs.length)__qs=base.search_queries.slice(0,3);\n  var __per=Math.min(Math.max(1,Number(__ci.max_items)||30),30);\n  var __overall=Math.min(__per*__qs.length,90);\n  var __mk=function(q){return 'https://www.avito.ru/moskva/predlozheniya_uslug?q='+encodeURIComponent(q);};\n  var __live=String(__ci.data_mode||'fixture').toLowerCase()==='live';\n  base.agent_called=true;\n  base.requested_by='agent';\n  base.agent_request_id=String(__ci.agent_request_id);\n  base.run_id=String(__ci.source_run_id||base.run_id);\n  base.workflow_run_id=String(__ci.workflow_run_id||'');\n  base.data_mode=__live?'live':'fixture';\n  base.fixture_mode=!__live;\n  base.live_mode=__live;\n  base.approval_token=String(__ci.approval_token||'');\n  base.max_budget_usd=Math.min((Number(__ci.max_budget_usd)>0?Number(__ci.max_budget_usd):base.max_budget_usd),1);\n  base.search_queries=__qs;\n  base.query_plan=__qs.map(function(q,i){return {query_index:i+1,search_query:q,source_search_url:__mk(q),allocation:__per};});\n  base.start_urls=base.query_plan.map(function(pp){return pp.source_search_url;});\n  base.actor_start_urls=base.query_plan.map(function(pp){return {url:pp.source_search_url,userData:{search_query:pp.search_query,source_search_url:pp.source_search_url,query_index:pp.query_index}};});\n  base.query_count=__qs.length;\n  base.per_query_allocation=__per;\n  base.max_items=__per;\n  base.live_max_items=__overall;\n  base.actor_limit=__overall;\n  base.overall_max_items=__overall;\n  base.pipeline_limit=__overall;\n}\nreturn [{ json: base }];\n"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000if",
      "name": "IF fixture_mode?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        560,
        520
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "cond-fixture-mode",
              "leftValue": "={{ $json.fixture_mode }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000bf",
      "name": "Build Fixture Avito Listings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        820,
        400
      ],
      "parameters": {
        "jsCode": "// Avito/classified listing fixtures. No external calls. In fixture mode max_items = total emitted records\n// (competitor listings + the optional irrelevant control record). DEC-092.\nconst cfg=$('Set Avito Connector Config').first().json;\nconst maxItems=Number(cfg.max_items)||6;\nconst includeControl=(cfg.include_irrelevant_control_fixture!==false);\nconst competitors=[\n  { listing_id:'4379480780', title:'\u041f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430. \u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440', description:'\u041f\u043e\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442 \u0444\u0438\u0437\u043b\u0438\u0446\u0430\u043c \u0438 \u0431\u0438\u0437\u043d\u0435\u0441\u0443. \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u043e \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0443, \u043e\u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. \u0421\u043b\u043e\u0436\u043d\u044b\u0435 \u0441\u043b\u0443\u0447\u0430\u0438, \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0441\u0443\u043c\u043c\u044b.', url:'https://www.avito.ru/moskva/predlozheniya_uslug/pomosch_v_poluchenii_kredita_kreditnyy_broker_4379480780', price:'\u043e\u0442 30 000 \u20bd', location:'\u041c\u043e\u0441\u043a\u0432\u0430', seller_name:'\u0424\u0438\u043d\u0430\u043d\u0441 \u0413\u0440\u0443\u043f\u043f', seller_url:'https://www.avito.ru/user/abc12300/profile', category:'\u041f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043b\u0443\u0433', published_at:'', query:'\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440 \u041c\u043e\u0441\u043a\u0432\u0430' },\n  { listing_id:'7961833832', title:'\u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440. \u0420\u0435\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u0440\u0435\u0434\u0438\u0442\u0430\u043c\u0438', description:'\u0420\u0435\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u0440\u0435\u0434\u0438\u0442\u0430\u043c\u0438 \u043f\u0440\u0438 \u043e\u0442\u043a\u0430\u0437\u0430\u0445 \u0431\u0430\u043d\u043a\u043e\u0432. \u041a\u043e\u043d\u0441\u0443\u043b\u044c\u0442\u0430\u0446\u0438\u044f \u043e\u0442 500 \u20bd. \u041c\u043e\u0441\u043a\u0432\u0430 \u0438 \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442\u044b.', url:'https://www.avito.ru/moskva/predlozheniya_uslug/kreditnyy_broker_realnaya_pomosch_7961833832', price:'\u043e\u0442 500 \u20bd', location:'\u041c\u043e\u0441\u043a\u0432\u0430', seller_name:'\u041a\u0440\u0435\u0434\u0438\u0442\u042d\u043a\u0441\u043f\u0435\u0440\u0442', seller_url:'https://www.avito.ru/user/def45600/profile', category:'\u041f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043b\u0443\u0433', published_at:'', query:'\u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430 \u041c\u043e\u0441\u043a\u0432\u0430' },\n  { listing_id:'5521099004', title:'\u041a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430, \u0418\u041f \u0438 \u041e\u041e\u041e. \u041a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440', description:'\u041f\u0440\u0438\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u0418\u041f \u0438 \u041e\u041e\u041e: \u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435 \u043a\u0440\u0435\u0434\u0438\u0442\u044b, \u0442\u0435\u043d\u0434\u0435\u0440\u043d\u044b\u0435 \u0437\u0430\u0439\u043c\u044b, \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438. \u041f\u043e\u043c\u043e\u0436\u0435\u043c \u0441 \u043f\u0430\u043a\u0435\u0442\u043e\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432.', url:'https://www.avito.ru/moskva/predlozheniya_uslug/kredit_dlya_biznesa_ip_ooo_5521099004', price:'\u043e\u0442 15 000 \u20bd', location:'\u041c\u043e\u0441\u043a\u0432\u0430', seller_name:'\u0411\u0438\u0437\u043d\u0435\u0441\u0424\u0438\u043d\u0430\u043d\u0441 \u041a\u043e\u043d\u0441\u0430\u043b\u0442', seller_url:'https://www.avito.ru/user/ghi78900/profile', category:'\u041f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043b\u0443\u0433', published_at:'', query:'\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 \u041c\u043e\u0441\u043a\u0432\u0430' },\n  { listing_id:'6610233771', title:'\u041a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u0431\u0430\u043d\u043a\u043e\u0432. \u041f\u043e\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c', description:'\u041f\u043e\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432, \u0441 \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a\u0430\u043c\u0438 \u0438 \u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439. \u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a\u0430.', url:'https://www.avito.ru/moskva/predlozheniya_uslug/kredit_posle_otkazov_bankov_6610233771', price:'', location:'\u041c\u043e\u0441\u043a\u0432\u0430', seller_name:'\u041e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u043524', seller_url:'https://www.avito.ru/user/jkl01200/profile', category:'\u041f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043b\u0443\u0433', published_at:'', query:'\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u041c\u043e\u0441\u043a\u0432\u0430' },\n  { listing_id:'7188450923', title:'\u0418\u043f\u043e\u0442\u0435\u0447\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440. \u0420\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043f\u043e\u0442\u0435\u043a\u0438', description:'\u041f\u043e\u0434\u0431\u043e\u0440 \u0438 \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435 \u0438\u043f\u043e\u0442\u0435\u043a\u0438, \u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0432, \u0441\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0432\u043a\u0438. \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e\u043c \u0431\u0430\u043d\u043a\u043e\u0432.', url:'https://www.avito.ru/moskva/predlozheniya_uslug/ipotechnyy_broker_refinansirovanie_7188450923', price:'\u043e\u0442 10 000 \u20bd', location:'\u041c\u043e\u0441\u043a\u0432\u0430', seller_name:'\u0418\u043f\u043e\u0442\u0435\u043a\u0430 \u041f\u0440\u043e\u0441\u0442\u043e', seller_url:'https://www.avito.ru/user/mno34500/profile', category:'\u041f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043b\u0443\u0433', published_at:'', query:'\u0438\u043f\u043e\u0442\u0435\u0447\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440 \u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u041c\u043e\u0441\u043a\u0432\u0430' }\n];\nconst control={ listing_id:'3300771199', title:'\u041f\u0440\u043e\u0434\u0430\u043c POS-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0434\u043b\u044f \u044d\u043a\u0432\u0430\u0439\u0440\u0438\u043d\u0433\u0430, \u0431/\u0443', description:'\u041f\u0440\u043e\u0434\u0430\u0451\u0442\u0441\u044f \u0431/\u0443 POS-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u0440\u0438\u0451\u043c\u0430 \u043a\u0430\u0440\u0442. \u0420\u0430\u0431\u043e\u0447\u0438\u0439, \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0435 \u0431\u043b\u043e\u043a \u043f\u0438\u0442\u0430\u043d\u0438\u044f. \u0421\u0430\u043c\u043e\u0432\u044b\u0432\u043e\u0437, \u041c\u043e\u0441\u043a\u0432\u0430.', url:'https://www.avito.ru/moskva/oborudovanie_dlya_biznesa/pos-terminal_dlya_ekvayringa_3300771199', price:'4 500 \u20bd', location:'\u041c\u043e\u0441\u043a\u0432\u0430', seller_name:'\u0427\u0430\u0441\u0442\u043d\u043e\u0435 \u043b\u0438\u0446\u043e', seller_url:'https://www.avito.ru/user/pqr67800/profile', category:'\u041e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430', published_at:'', query:'\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440 \u041c\u043e\u0441\u043a\u0432\u0430' };\nlet chosen;\nif(includeControl){ chosen=competitors.slice(0,Math.max(0,maxItems-1)); if(chosen.length<maxItems) chosen=chosen.concat([control]); }\nelse { chosen=competitors.slice(0,maxItems); }\nreturn chosen.map((r,i)=>({ json: Object.assign({ _index:i+1, _source:'fixture' }, r) }));"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000aq0",
      "name": "Apify Avito Classifieds Actor Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        820,
        640
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "=https://api.apify.com/v2/acts/{{ $json.apify_actor_id }}/run-sync-get-dataset-items",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"limit\": {{ $json.actor_limit || 10 }},\n  \"startUrls\": {{ JSON.stringify($json.start_urls) }},\n  \"proxyConfiguration\": {{ JSON.stringify($json.proxy_config || {\"useApifyProxy\":true}) }}\n}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000nz",
      "name": "Normalize Avito Listings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        520
      ],
      "parameters": {
        "jsCode": "// Deterministic normalization of Avito/classified listings into raw_market_records shape. No LLM, no external calls.\n// DEC-092: richer service_hint + concrete keyword phrases. DEC-094: strict valid-listing guard.\n// DEC-095: live BUSINESS RELEVANCE FILTER \u2014 relevance is judged ONLY on listing evidence\n// (title + description + decoded URL slug + category). The search query alone NEVER makes a listing\n// relevant; query-only / hard-negative live items are marked hard_skip and are not written downstream.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction low(v){return str(v).toLowerCase();}\nfunction any(b,arr){for(const w of arr){if(b.indexOf(w)>=0)return true;}return false;}\nfunction hits(b,arr){const out=[];for(const w of arr){if(b.indexOf(w)>=0)out.push(w);}return out;}\n// Sandbox-safe URL helpers (DEC-103): pure regex/string, no URL-constructor dependency \u2014 the n8n Code-node\n// sandbox may not expose the URL global; the old try/catch fallback silently kept tracking query params\n// (?context=...) in live runs and blanked the decoded-slug relevance evidence.\nfunction normUrl(u){u=str(u);if(!u)return '';u=u.split('#')[0];var m=u.match(/^(https?):\\/\\/([^\\/?]+)([^?]*)(\\?.*)?$/i);if(!m)return u.toLowerCase();var path=m[3]||'/';if(path.length>1&&path.slice(-1)==='/')path=path.slice(0,-1);return m[1].toLowerCase()+'://'+m[2].toLowerCase()+(path||'/')+(m[4]||'');}\n// Canonical Avito listing URL: strip query/hash ONLY when the path carries a listing id (safe).\nfunction canonUrl(u){u=str(u);if(!u)return '';var base=u.split('#')[0].split('?')[0];if(base.length>1&&base.slice(-1)==='/')base=base.slice(0,-1);var path=base.replace(/^https?:\\/\\/[^\\/]+/i,'');if(/_(\\d{6,})$/.test(path)||/\\/(\\d{7,})$/.test(path)){return base;}return u;}\n// Decoded URL slug (\"yuridicheskiy_adres_dlya_ooo_\u2026\" -> \"yuridicheskiy adres dlya ooo \u2026\") as relevance evidence.\nfunction slugText(u){u=str(u);if(!u)return '';var path=u.split('#')[0].split('?')[0].replace(/^https?:\\/\\/[^\\/]+/i,'');var segs=path.split('/').filter(Boolean);var s=segs.length?segs[segs.length-1]:'';s=s.replace(/_\\d{6,}$/,'');return s.replace(/[_-]+/g,' ').toLowerCase();}\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 cut(s,n){s=str(s);return s.length>n?s.slice(0,n):s;}\nfunction detectPlaceholderTitle(t){t=str(t);if(!t)return false;if(/\u0435\u0449[\u0435\u0451]\\s*\\d+\\s*\u0444\u043e\u0442\u043e/i.test(t))return true;if(/^\\s*\\+?\\d+\\s*\u0444\u043e\u0442\u043e\\s*$/i.test(t))return true;if(/^\\s*\\d+\\s*(\u0444\u043e\u0442\u043e|photos?|images?)\\s*$/i.test(t))return true;return false;}\nfunction kwHit(blob,tok){ if(tok.length<=3){ return new RegExp('(^|[^\u0430-\u044f\u0451a-z0-9])'+tok+'([^\u0430-\u044f\u0451a-z0-9]|$)','i').test(blob);} return blob.indexOf(tok)>=0; }\n// [match token, display label]\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'],['\u0438\u043f\u043e\u0442\u0435\u0447\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','\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','\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','\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432'],['\u043e\u0442\u043a\u0430\u0437\u0430\u0445 \u0431\u0430\u043d\u043a','\u043e\u0442\u043a\u0430\u0437\u044b \u0431\u0430\u043d\u043a\u043e\u0432'],['\u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f'],['\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f'],['\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a','\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a\u0438'],['\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'],['\u043f\u043e \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0443','\u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u043e \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0443'],['\u043a\u043e\u043d\u0441\u0443\u043b\u044c\u0442\u0430\u0446\u0438','\u043a\u043e\u043d\u0441\u0443\u043b\u044c\u0442\u0430\u0446\u0438\u044f'],['\u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a','\u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a\u0430'],['\u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435','\u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435 \u043a\u0440\u0435\u0434\u0438\u0442\u0430'],['\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0441\u0443\u043c\u043c\u044b','\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0441\u0443\u043c\u043c\u044b'],['\u0438\u043f','\u0418\u041f'],['\u043e\u043e\u043e','\u041e\u041e\u041e'],['\u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u0440\u0435\u0434\u0438\u0442','\u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u0440\u0435\u0434\u0438\u0442'],['\u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u0440\u0435\u0434\u0438\u0442'],['\u043e\u0431\u043e\u0440\u043e\u0442\u043d','\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435 \u043a\u0440\u0435\u0434\u0438\u0442\u044b'],['\u0442\u0435\u043d\u0434\u0435\u0440\u043d','\u0442\u0435\u043d\u0434\u0435\u0440\u043d\u044b\u0435 \u0437\u0430\u0439\u043c\u044b'],['\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a','\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','\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','\u0441\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0432\u043a\u0438'],['\u0431\u0435\u0437 \u043f\u043e\u0440\u0443\u0447\u0438\u0442\u0435\u043b','\u0431\u0435\u0437 \u043f\u043e\u0440\u0443\u0447\u0438\u0442\u0435\u043b\u0435\u0439'],['\u0431\u0435\u0437 \u0441\u043f\u0440\u0430\u0432\u043e\u043a','\u0431\u0435\u0437 \u0441\u043f\u0440\u0430\u0432\u043e\u043a']];\nfunction extractKeywords(blob){ const seen={}; const out=[]; for(const pair of KW){ if(kwHit(blob,pair[0]) && !seen[pair[1]]){ seen[pair[1]]=1; out.push(pair[1]); } } return out.join(', '); }\n// ---- DEC-095 relevance term sets (Cyrillic + transliterated forms for decoded Avito slugs) ----\nconst STRONG_POS=['\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0439 \u0431\u0440\u043e\u043a\u0435\u0440','\u043a\u0440\u0435\u0434\u0438\u0442\u043d\u044b\u0435 \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','\u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u0440\u0435\u0434\u0438\u0442','\u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043a\u0440\u0435\u0434\u0438\u0442\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u043f\u043e\u0442\u0435\u043a','\u043a\u0440\u0435\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u043a\u0440\u0435\u0434\u0438\u0442\u044b \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438','\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0430\u044f \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f','kreditnyy broker','kreditnii broker','kreditny broker','ipotechnyy broker','ipotechnii broker','pomosch v poluchenii','pomoshch v poluchenii','pomosch s kreditom','kredit posle otkazov','refinansirovanie ipotek','kredit dlya biznesa','bankovskie garantii','bankovskaya garantiya'];\nconst WEAK_POS=['\u043a\u0440\u0435\u0434\u0438\u0442','\u0437\u0430\u0439\u043c','\u0437\u0430\u0439\u043c\u044b','\u0438\u043f\u043e\u0442\u0435\u043a','\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440','\u043e\u0442\u043a\u0430\u0437\u043e\u0432 \u0431\u0430\u043d\u043a\u043e\u0432','\u043e\u0442\u043a\u0430\u0437\u044b \u0431\u0430\u043d\u043a\u043e\u0432','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d','\u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d','\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a','\u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u0435 \u043a\u0440\u0435\u0434\u0438\u0442','\u043f\u043e\u0434\u0431\u043e\u0440 \u0431\u0430\u043d\u043a','\u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u0440\u0435\u0434\u0438\u0442','\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0435 \u043a\u0440\u0435\u0434\u0438\u0442','\u043e\u0431\u043e\u0440\u043e\u0442\u043d\u044b\u0439 \u043a\u0440\u0435\u0434\u0438\u0442','\u0442\u0435\u043d\u0434\u0435\u0440\u043d\u044b\u0435 \u0437\u0430\u0439\u043c','\u0442\u0435\u043d\u0434\u0435\u0440\u043d\u044b\u0439 \u0437\u0430\u0439\u043c','\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0438\u0437\u043d\u0435\u0441','\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0432\u0430\u043d','kredit','zaym','zaim','ipotek','refinansir','otkaz','prosrochk','odobrenie kredit','podbor banka','finansirovanie'];\nconst HARD_NEG=['\u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u0434\u0440\u0435\u0441','\u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0430\u0434\u0440\u0435\u0441','\u044e\u0440 \u0430\u0434\u0440\u0435\u0441','\u044e\u0440. \u0430\u0434\u0440\u0435\u0441','\u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u043e\u043e\u043e','\u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438','\u043d\u0435\u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439','\u043d\u0435 \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439','\u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 \u0430\u0434\u0440\u0435\u0441','\u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 \u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a','\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','\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043e\u043e\u043e','\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438\u043f','\u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440\u0441\u043a','\u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440\u0438','\u044d\u043a\u0432\u0430\u0439\u0440\u0438\u043d\u0433','pos-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b','pos \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b','\u043f\u043e\u0441-\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b','\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b','\u043e\u043d\u043b\u0430\u0439\u043d-\u043a\u0430\u0441\u0441','\u043a\u0430\u0441\u0441\u0430','\u043a\u0430\u0441\u0441\u0443','\u043a\u0430\u0441\u0441\u044b','\u043f\u0435\u0447\u0430\u0442\u044c','\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','\u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0443\u0441\u043b\u0443\u0433\u0438','\u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435','yuridicheskiy adres','yuridichesky adres','yuridicheskii adres','adres dlya ooo','nemassovyy','ne massovyy','massovyy adres','massovyy yuridicheskiy','registratsiya ooo','registratsiya ip','buhgalter','bukhgalter','ekvayring','ekvairing','pos terminal','pos-terminal','terminal','kassa','onlayn kassa','pechat','shtamp','arenda ofisa','kovorking','yuridicheskie uslugi','oborudovanie'];\nfunction scoreRelevance(blob){\n  const sp=hits(blob,STRONG_POS);\n  const hn=hits(blob,HARD_NEG);\n  const wp=hits(blob,WEAK_POS);\n  if(sp.length)return {level:'strong_positive',matched:sp,hard_neg:hn};\n  if(hn.length)return {level:'hard_negative',matched:hn,hard_neg:hn};\n  if(wp.length)return {level:'weak_positive',matched:wp,hard_neg:[]};\n  return {level:'none',matched:[],hard_neg:[]};\n}\nconst cfg=$('Set Avito Connector Config').first().json;\nconst fixtureMode=(cfg.fixture_mode===true||low(cfg.fixture_mode)==='true');\nconst defFocus=low(cfg.service_focus)||'credit_broker';\nconst region=str(cfg.region)||'\u041c\u043e\u0441\u043a\u0432\u0430/\u041c\u041e';\nconst items=$input.all().map(i=>i.json).filter(Boolean);\nconst out=[];\nfor(const r of items){\n  // Field aliases: fixture fields OR varied live actor (fatihtahta~avito-russia-scraper) shapes. Do not invent data.\n  const title=str(r.title||r.name||r.heading);\n  const description=cut(str(r.description||r.text),800);\n  const price=str(r.priceText||r.price||r.price_text);\n  const location=str(r.location||r.address)||region;\n  const seller_name=str(r.sellerName||r.seller||r.seller_name||r.userName);\n  const seller_url=normUrl(r.seller_url||r.sellerUrl||r.profileUrl);\n  const category=str(r.category);\n  const urlRaw=str(r.url||r.sourceUrl||r.source_url||r.listingUrl||r.adUrl||r.link);\n  const url=canonUrl(normUrl(urlRaw));\n  const parentSourceUrl=str(r.parentSourceUrl);\n  // \u00a78.2 query separation: search_query = human phrase; source_search_url = encoded URL. The encoded URL must\n  // NEVER land in interest_topic/probable_need/semantic_keywords/offer text.\n  let search_query_human=str(r.query)||str(r.search_query)||(r.userData?str(r.userData.search_query):'');\n  let source_search_url=parentSourceUrl||(r.userData?str(r.userData.source_search_url):'');\n  if(parentSourceUrl){\n    const _pn=normUrl(parentSourceUrl);\n    const _plan=Array.isArray(cfg.query_plan)?cfg.query_plan:[];\n    for(const _p of _plan){ if(normUrl(str(_p.source_search_url))===_pn){ if(search_query_human==='')search_query_human=str(_p.search_query); if(source_search_url==='')source_search_url=str(_p.source_search_url); break; } }\n  }\n  // Patch 5 (Obj5): if the origin query is known but its URL is not, recover source_search_url from the\n  // matching query_plan entry (its specific search URL) \u2014 never start_urls[0] / the concatenated query list.\n  if(source_search_url===''&&search_query_human!==''){ const _plan2=Array.isArray(cfg.query_plan)?cfg.query_plan:[]; for(const _q of _plan2){ if(str(_q.search_query)===search_query_human){ source_search_url=str(_q.source_search_url); break; } } }\n  if(/^https?:\\/\\//i.test(search_query_human)){ if(!source_search_url)source_search_url=search_query_human; search_query_human=''; }\n  const query=search_query_human;\n  // Obj2: exported origin is the SPECIFIC per-record query, else 'unknown' \u2014 never the concatenated query list.\n  const search_query_origin=(str(search_query_human)!=='')?search_query_human:'unknown';\n  const lidMatch=urlRaw.match(/_(\\d{6,})(?:[/?#]|$)/)||urlRaw.match(/\\/(\\d{7,})(?:[/?#]|$)/);\n  const listing_id=str(r.listing_id||(lidMatch?lidMatch[1]:''));\n  const published_at=str(r.validFrom||r.published_at);\n  // DEC-094: strict valid-listing guard (live actor may return empty/search-only items).\n  const startSet=new Set((Array.isArray(cfg.start_urls)?cfg.start_urls:[]).map(function(u){return normUrl(u);}));\n  function looksLikeAvitoListing(u){\n    if(!u)return false;\n    if(!/^https?:\\/\\/(www\\.)?avito\\.ru\\//i.test(u))return false;\n    if(startSet.has(u))return false;\n    if(/[?&]q=/i.test(u))return false;\n    if(!/_(\\d{6,})(?:[/?#]|$)/.test(u) && !/\\/(\\d{7,})(?:[/?#]|$)/.test(u))return false;\n    return true;\n  }\n  const hasContent=(title!=='')||(description!=='')||(price!=='');\n  const is_valid_listing=looksLikeAvitoListing(url)&&hasContent;\n  let invalid_reason='';\n  if(!is_valid_listing){\n    if(!url)invalid_reason='no_listing_url';\n    else if(!/^https?:\\/\\/(www\\.)?avito\\.ru\\//i.test(url))invalid_reason='not_avito_url';\n    else if(startSet.has(url)||/[?&]q=/i.test(url))invalid_reason='search_or_start_url_not_a_listing';\n    else if(!/_(\\d{6,})(?:[/?#]|$)/.test(url)&&!/\\/(\\d{7,})(?:[/?#]|$)/.test(url))invalid_reason='no_listing_id_in_url';\n    else invalid_reason='no_title_price_or_description';\n  }\n  const classBlob=(title+' '+description+' '+category).toLowerCase();\n  const kwBlob=(title+' '+description+' '+query).toLowerCase();\n  // DEC-095: relevance evidence = title + description + decoded slug + category. Query is EXCLUDED on purpose.\n  const slug=slugText(urlRaw);\n  const evidenceBlob=(title+' '+description+' '+slug+' '+category).toLowerCase();\n  const rel=scoreRelevance(evidenceBlob);\n  let record_type_hint, touchpoint_type, predicted_route, predicted_entity_type, next_action, lead_temperature, confidence_score;\n  let hard_skip=false, relevance_reason='';\n  if(rel.level==='strong_positive'){\n    record_type_hint='competitor_activity'; touchpoint_type='competitor_listing'; predicted_route='monitor_queue'; predicted_entity_type='competitor'; next_action='monitor'; lead_temperature='cold'; confidence_score=price?82:74;\n    relevance_reason='strong credit/broker evidence: '+rel.matched.slice(0,4).join(', ')+(rel.hard_neg.length?(' (hard-neg terms also present: '+rel.hard_neg.slice(0,3).join(', ')+')'):'');\n  } else if(rel.level==='hard_negative'){\n    relevance_reason='hard-negative evidence without strong credit/broker evidence: '+rel.matched.slice(0,4).join(', ');\n    touchpoint_type='irrelevant_source'; predicted_route='skipped_log'; predicted_entity_type='irrelevant'; next_action='ignore'; lead_temperature='none';\n    if(fixtureMode){ record_type_hint='irrelevant'; confidence_score=40; }\n    else { record_type_hint='irrelevant_live_false_positive'; confidence_score=30; hard_skip=true; }\n  } else if(rel.level==='weak_positive'){\n    record_type_hint='market_signal'; touchpoint_type='classified_offer'; predicted_route='content_queue'; predicted_entity_type='content_idea'; next_action='create_content'; lead_temperature='cold'; confidence_score=60;\n    relevance_reason='weak finance evidence in title/description/slug/category: '+rel.matched.slice(0,4).join(', ');\n  } else {\n    relevance_reason='no listing-evidence relevance (query-only context rejected)';\n    touchpoint_type='irrelevant_source'; predicted_route='skipped_log'; predicted_entity_type='irrelevant'; next_action='ignore'; lead_temperature='none';\n    if(fixtureMode){ record_type_hint='irrelevant'; confidence_score=40; }\n    else { record_type_hint='irrelevant_live_false_positive'; confidence_score=30; hard_skip=true; }\n  }\n  // \u00a78.2/\u00a78.3 search-card & placeholder quarantine: an Apify search card (isDetail=false) or an '\u0415\u0449\u0451 N \u0444\u043e\u0442\u043e'\n  // placeholder is NOT a full listing. Mark it source_candidate + detail_fetch_required; never a competitor offer.\n  const placeholder_title=detectPlaceholderTitle(title);\n  const _isDetailRaw=(r.isDetail!==undefined?r.isDetail:(r.is_detail!==undefined?r.is_detail:undefined));\n  const is_detail=(_isDetailRaw===true||low(_isDetailRaw)==='true');\n  const _explicitNotDetail=(_isDetailRaw===false||low(_isDetailRaw)==='false');\n  const search_card=_explicitNotDetail||((placeholder_title||description==='')&&(seller_name===''||published_at===''));\n  const detail_fetch_required=placeholder_title||search_card;\n  if(detail_fetch_required){\n    record_type_hint='source_candidate'; touchpoint_type='search_card'; predicted_route='review_queue';\n    predicted_entity_type='source_candidate'; next_action='enrich_detail'; lead_temperature='none';\n    confidence_score=placeholder_title?20:30; hard_skip=false;\n    relevance_reason='search_card_no_detail (isDetail=false / placeholder title); detail_fetch_required';\n  }\n  const isIrrelevant=(record_type_hint==='irrelevant'||record_type_hint==='irrelevant_live_false_positive');\n  const competitor_related=(record_type_hint==='competitor_activity');\n  const competitor_name=competitor_related?seller_name:'';\n  // service_hint (DEC-092): business_credit only when \u0418\u041f/\u041e\u041e\u041e/business financing is EXPLICIT (not bare '\u0431\u0438\u0437\u043d\u0435\u0441\u0443').\n  let service_hint=defFocus;\n  if(isIrrelevant)service_hint='unknown';\n  else if(any(classBlob,['\u0438\u043f\u043e\u0442\u0435\u043a','\u0440\u0435\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440','\u0441\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0432\u043a']))service_hint='mortgage_refinance';\n  else if(any(classBlob,['\u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430','\u0438\u043f \u0438 \u043e\u043e\u043e','\u043e\u0431\u043e\u0440\u043e\u0442\u043d','\u0442\u0435\u043d\u0434\u0435\u0440\u043d','\u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a','\u0431\u0438\u0437\u043d\u0435\u0441-\u043a\u0440\u0435\u0434\u0438\u0442']))service_hint='business_credit';\n  else if(any(classBlob,['\u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432','\u043e\u0442\u043a\u0430\u0437\u0430\u0445 \u0431\u0430\u043d\u043a','\u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a','\u043f\u043b\u043e\u0445\u043e\u0439 \u043a\u0440\u0435\u0434\u0438\u0442\u043d','\u043f\u043b\u043e\u0445\u0430\u044f \u043a\u0440\u0435\u0434\u0438\u0442\u043d']))service_hint='credit_after_refusals';\n  else service_hint='credit_broker';\n  let probable_need='';\n  if(!isIrrelevant){\n    if(service_hint==='credit_after_refusals')probable_need='\u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0440\u0435\u0434\u0438\u0442 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0430\u0437\u043e\u0432 / \u0441 \u043f\u043b\u043e\u0445\u043e\u0439 \u041a\u0418';\n    else if(service_hint==='business_credit')probable_need='\u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430 (\u0418\u041f/\u041e\u041e\u041e)';\n    else if(service_hint==='mortgage_refinance')probable_need='\u0438\u043f\u043e\u0442\u0435\u043a\u0430 / \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';\n    else probable_need='\u043f\u043e\u043c\u043e\u0449\u044c \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u0430';\n  }\n  const semantic_keywords=isIrrelevant?'':extractKeywords(kwBlob);\n  const offer_short=cut(title+(price?(' \u2014 '+price):''),140);\n  const _isCard=(record_type_hint==='source_candidate');\n  // \u00a713/Obj2 explicit per-record observability for WF16 classification.\n  var _qf=[];\n  if(detail_fetch_required){_qf.push('search_card');_qf.push('no_detail_record');}\n  if(placeholder_title)_qf.push('placeholder_title');\n  if(seller_name==='')_qf.push('missing_seller_identity');\n  if(description==='')_qf.push('missing_description');\n  if(published_at==='')_qf.push('missing_published_at');\n  if(hard_skip)_qf.push('hard_skip_irrelevant');\n  if(isIrrelevant)_qf.push('irrelevant');\n  const quality_flags=_qf.join('; ');\n  const activity_subtype=_isCard?'classified_search_card':(competitor_related?'classified_direct_offer':(record_type_hint==='market_signal'?'classified_market_signal':(isIrrelevant?'irrelevant_listing':'classified_offer')));\n  const skip_reason=_isCard?'detail_enrichment_required':(hard_skip?'irrelevant_no_listing_evidence':(record_type_hint==='irrelevant'?'irrelevant_control':''));\n  const manager_note=isIrrelevant\n    ? ('Avito/classified: \u043d\u0435\u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 ('+relevance_reason+'), \u043f\u0440\u043e\u043f\u0443\u0441\u043a.')\n    : (_isCard\n      ? ('Avito search card (isDetail=false/placeholder) \u2014 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f detail-enrichment \u043f\u0435\u0440\u0435\u0434 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u043e\u043c \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u0430; offer \u043d\u0435 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d.')\n      : '\u041e\u0444\u0444\u0435\u0440 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u0430: '+offer_short+(price?(' | \u0446\u0435\u043d\u0430: '+price):' | \u0446\u0435\u043d\u0430: \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430')+' | \u0443\u0441\u043b\u043e\u0432\u0438\u044f/\u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430: '+(semantic_keywords||'\u2014')+' | \u043f\u043b\u043e\u0449\u0430\u0434\u043a\u0430/\u043a\u0430\u043d\u0430\u043b: Avito (classifieds).');\n  const text_context=cut('Avito \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435: '+title+(price?(' | \u0446\u0435\u043d\u0430: '+price):'')+' | '+location+(seller_name?(' | \u043f\u0440\u043e\u0434\u0430\u0432\u0435\u0446: '+seller_name):'')+(description?(' | '+description):'')+' | \u0437\u0430\u043f\u0440\u043e\u0441: '+query, 1200);\n  const text_hash=hashText(low(title)+'||'+low(description)+'||'+low(price));\n  let basekey;\n  if(listing_id)basekey='avito_listing_'+listing_id;\n  else basekey='avito_url_'+hashText(url||text_context);\n  const dedup_key=low(cfg.platform||'avito')+'::'+low(cfg.source_type||'classified')+'::'+basekey;\n  out.push({ json: {\n    source_type:low(cfg.source_type)||'classified',\n    platform:low(cfg.platform)||'avito',\n    source_url:url,\n    post_url:url,\n    profile_url:seller_url,\n    profile_name:seller_name,\n    author_handle:'',\n    seller_name:seller_name,\n    published_at:published_at,\n    region_hint:region,\n    service_hint:service_hint,\n    query:query,\n    text_context:text_context,\n    comment_text:'',\n    contact_public:'',\n    contact_channel:'',\n    dedup_key:dedup_key,\n    text_hash:text_hash,\n    record_type_hint:record_type_hint,\n    touchpoint_type:touchpoint_type,\n    lead_intent_hint:'low',\n    urgency_hint:'low',\n    interest_topic:query,\n    probable_need:probable_need,\n    competitor_related:competitor_related,\n    competitor_name:competitor_name,\n    semantic_keywords:semantic_keywords,\n    ad_channel_hint:'classifieds',\n    confidence_score:confidence_score,\n    lead_temperature:lead_temperature,\n    next_action:next_action,\n    predicted_route:predicted_route,\n    predicted_entity_type:predicted_entity_type,\n    responsible:'',\n    estimated_analysis_cost_usd:0,\n    manager_note:manager_note,\n    listing_id:listing_id,\n    price:price,\n    location:location,\n    title:title,\n    description:description,\n    is_valid_listing:is_valid_listing,\n    invalid_reason:invalid_reason,\n    business_relevance:rel.level,\n    relevance_matched:rel.matched.slice(0,6).join(', '),\n    relevance_reason:relevance_reason,\n    hard_skip:hard_skip,\n    // \u00a78.2/\u00a78.3/\u00a713 hardening fields\n    data_mode:(fixtureMode?'fixture':'live'),\n    is_detail:is_detail,\n    placeholder_title:placeholder_title,\n    detail_fetch_required:detail_fetch_required,\n    detail_fetch_status:(detail_fetch_required?'pending':'not_required'),\n    search_query:search_query_origin,\n    source_search_url:source_search_url,\n    exact_evidence_url:(!detail_fetch_required && !!url && hasContent),\n    evidence_completeness_score:(function(){var s=0;if(description.length>=80)s+=30;else if(description.length>=30)s+=15;if(url)s+=20;if(seller_name)s+=15;if(published_at)s+=15;if(title&&!placeholder_title)s+=20;return s;})(),\n    quality_status:(isIrrelevant||hard_skip?'quarantined':(detail_fetch_required?'degraded':'healthy')),\n    llm_eligible:!(detail_fetch_required||isIrrelevant||hard_skip),\n    report_eligible:((fixtureMode?'fixture':'live')==='live')&&!(detail_fetch_required||isIrrelevant||hard_skip),\n    quality_flags:quality_flags,\n    activity_subtype:activity_subtype,\n    skip_reason:skip_reason,\n    taxonomy_version:'semantic-v2.0'\n  }});\n}\nreturn out;"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000rg",
      "name": "Read market_record_registry",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1320,
        520
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "={{ $env.MS_SPREADSHEET_ID || \"PASTE_SPREADSHEET_ID\" }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "market_record_registry",
          "mode": "name"
        },
        "options": {}
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000dd",
      "name": "Deduplicate Listings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        520
      ],
      "parameters": {
        "jsCode": "// Dedup normalized listings vs market_record_registry (by dedup_key) + within batch. No external calls.\n// DEC-094: drop INVALID live items (empty/search-only) before dedup.\n// DEC-095: hard-skip live false positives (hard-negative / query-only) BEFORE pipeline_limit \u2014\n// pipeline_limit applies only to accepted business-relevant records; hard negatives never consume the cap.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Avito Connector Config').first().json;\nconst approvalUnique=str(cfg.approval_status_for_unique)||'new';\nconst dupNext=str(cfg.duplicate_next_action)||'monitor_duplicate';\nconst fixtureMode=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nconst pipelineLimit=Number(cfg.pipeline_limit)||Number(cfg.live_max_items)||10;\nconst recs=$('Normalize Avito Listings').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  // Invalid live items: never registered, never counted unique, never written as competitor.\n  if(r.is_valid_listing===false){\n    out.push({ json: Object.assign({}, r, { dedup_status:'invalid', approval_status:'invalid', next_action:'ignore' }) });\n    continue;\n  }\n  // DEC-095: live hard-negative / query-only false positives are filtered BEFORE the pipeline cap\n  // and are NOT written to raw_market_records (by default) or market_record_registry.\n  if(!fixtureMode && r.hard_skip===true){\n    out.push({ json: Object.assign({}, r, { dedup_status:'hard_skipped', approval_status:'skipped_irrelevant', next_action:'ignore' }) });\n    continue;\n  }\n  // Live pipeline cap: write at most pipeline_limit accepted business-relevant listings.\n  if(!fixtureMode && acceptedCount>=pipelineLimit){\n    out.push({ json: Object.assign({}, r, { dedup_status:'over_pipeline_limit', approval_status:'skipped', next_action:'ignore' }) });\n    continue;\n  }\n  acceptedCount++;\n  const key=str(r.dedup_key);\n  let dedup_status='unique';\n  let approval_status=approvalUnique;\n  let next_action=str(r.next_action);\n  if(key&&regSet.has(key)){\n    dedup_status='duplicate_in_registry';approval_status='duplicate';\n    next_action=(str(r.record_type_hint)==='irrelevant')?'ignore':dupNext;\n  } else if(key&&seen.has(key)){\n    dedup_status='duplicate_in_batch';approval_status='duplicate';\n    next_action=(str(r.record_type_hint)==='irrelevant')?'ignore':dupNext;\n  } else {\n    dedup_status='unique';if(key)seen.add(key);\n  }\n  out.push({ json: Object.assign({}, r, { dedup_status:dedup_status, approval_status:approval_status, next_action:next_action }) });\n}\nreturn out;"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000brw",
      "name": "Build raw_market_records Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        520
      ],
      "parameters": {
        "jsCode": "// Build raw_market_records rows (Workflow 07 schema + \u00a713 runtime observability columns). No external calls.\nfunction str(v){return v==null?'':String(v).trim();}\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 Avito Connector Config').first().json;\nconst reqId=str(cfg.agent_request_id);\nconst created=str(cfg.created_at);\nconst stamp=str(cfg.run_id).replace(/^avito_/,'')||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 Listings').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  // DEC-095: hard-skipped live false positives are NOT written by default (no raw pollution); debug audit opt-in.\n  if(ds0==='hard_skipped'&&!hardDebug)continue;\n  if(!writeDupAudit && ds0!=='unique')continue;\n  out.push({ json: {\n    record_id:'avito_rec_'+stamp+'_'+idx,\n    agent_request_id:reqId,\n    source_run_id:str(cfg.run_id)||reqId,\n    data_mode:str(cfg.data_mode)||'live',\n    quality_status:str(r.quality_status)||'healthy',\n    report_eligible:(r.report_eligible===true||str(r.report_eligible).toLowerCase()===\"true\"),\n    review_status:((r.detail_fetch_required===true||str(r.detail_fetch_required).toLowerCase()===\"true\")?\"pending\":\"confirmed\"),\n    quality_flags:str(r.quality_flags),\n    llm_eligible:(r.llm_eligible===true||str(r.llm_eligible).toLowerCase()==='true'),\n    is_detail:(r.is_detail===true||str(r.is_detail).toLowerCase()==='true'),\n    placeholder_title:(r.placeholder_title===true||str(r.placeholder_title).toLowerCase()==='true'),\n    detail_fetch_required:(r.detail_fetch_required===true||str(r.detail_fetch_required).toLowerCase()==='true'),\n    detail_fetch_status:str(r.detail_fetch_status),\n    exact_evidence_url:(r.exact_evidence_url===true||str(r.exact_evidence_url).toLowerCase()==='true'),\n    search_query:str(r.search_query),\n    source_search_url:str(r.source_search_url),\n    activity_subtype:str(r.activity_subtype),\n    skip_reason:str(r.skip_reason),\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:str(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:'stage_3_3_avito_classifieds_connector'\n  }});\n}\n// AGENT-SUMMARY-001: 0 collected rows must NOT kill the summary/ledger chain \u2014 emit a marker\n// item so the run still writes its live_source_runs ledger row (WF16/08/10/12 trust the ledger).\nif(out.length===0){return [{json:{ms_no_rows:true}}];}\nreturn out;"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000ar",
      "name": "Append raw_market_records",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1980,
        520
      ],
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $env.MS_SPREADSHEET_ID || \"PASTE_SPREADSHEET_ID\" }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "raw_market_records",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000brr",
      "name": "Build market_record_registry Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2200,
        380
      ],
      "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 Listings').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.seller_name||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 Avito Connector Config').first().json.agent_request_id),\n    note:'avito_classifieds_connector_stage_3_3'\n  }});\n}\nreturn out;"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000rr",
      "name": "Append market_record_registry",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        2420,
        380
      ],
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $env.MS_SPREADSHEET_ID || \"PASTE_SPREADSHEET_ID\" }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "market_record_registry",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000bq",
      "name": "Build agent_requests Row",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2200,
        660
      ],
      "parameters": {
        "jsCode": "// One agent_requests row (exactly 21 columns). status=completed. No external calls.\n// DEC-094/095: result_summary reports actor_items_received / structurally_valid_items / invalid_items /\n// business_relevant_items / hard_skipped_items / unique / duplicates / over_pipeline_limit.\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 Avito Connector Config').first().json;\n// agent-called mode: the canonical agent_requests row was already written by WF18 \u2014 never a second (legacy-shaped) one.\nif(cfg.agent_called===true){return [{json:{ms_agent_mode:true}}];} // AGENT-SUMMARY-001: marker keeps the ledger chain alive; the IF below bypasses the legacy append\nconst recs=$('Deduplicate Listings').all().map(i=>i.json);\nconst actor_items_received=recs.length;\nconst invalid_items=recs.filter(r=>str(r.dedup_status)==='invalid').length;\nconst structurally_valid_items=actor_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 skipped=recs.filter(r=>str(r.predicted_route)==='skipped_log'&&['invalid','over_pipeline_limit','hard_skipped'].indexOf(str(r.dedup_status))<0).length;\nconst queries=Array.isArray(cfg.search_queries)?cfg.search_queries.join('; '):str(cfg.query);\nconst fixtureMode=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nlet notes='No auto-handoff in v0.1. Competitor Ad / Semantic Intelligence connector. Source: '+(fixtureMode?'fixture':'apify_live')+'.';\nif(invalid_items>0){\n  notes+=' Live Apify returned item(s), but no valid listing_url/title/price fields were found. Inspect Apify node output and actor schema.';\n  const firstInvalid=recs.filter(r=>str(r.dedup_status)==='invalid')[0];\n  if(firstInvalid){ notes+=' raw_response_preview: '+cut(str(firstInvalid.text_context||firstInvalid.query),300); }\n}\nif(hard_skipped_items>0){\n  notes+=' hard_skipped_items='+hard_skipped_items+' live false positive(s) (hard-negative/query-only, DEC-095) filtered BEFORE pipeline_limit; not written to raw_market_records or market_record_registry.';\n  const firstHard=recs.filter(r=>str(r.dedup_status)==='hard_skipped')[0];\n  if(firstHard){ notes+=' first_hard_skip: '+cut(str(firstHard.title||firstHard.source_url),120)+' ['+cut(str(firstHard.relevance_reason),160)+']'; }\n}\nif(over_pipeline_limit>0){ notes+=' over_pipeline_limit='+over_pipeline_limit+' (business-relevant listings beyond pipeline_limit were not written).'; }\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:'Avito/classified competitor discovery: '+queries,\n  request_type:str(cfg.request_type)||'classified_competitor_discovery',\n  source_scope:'classified_listings',\n  platforms:'avito',\n  query:queries,\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:fixtureMode?actor_items_received:(Number(cfg.pipeline_limit)||Number(cfg.live_max_items)||10),\n  status:'completed',\n  plan_summary:'Avito/Classifieds listing connector ('+(fixtureMode?'fixture mode':'live Apify mode')+') -> raw_market_records for the Touchpoint Analyzer (Workflow 08).',\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:'actor_items_received='+actor_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+'.',\n  next_action:(unique>0?'Run Workflow 08 on collected raw records manually':(business_relevant_items>0?'All business-relevant items were duplicates; registry unchanged \u2014 review duplicates, no Workflow 08 run needed':'Do NOT run Workflow 08 (no business-relevant unique rows): inspect Apify output / relevance filter, then re-run live smoke')),\n  notes:notes\n}}];"
      }
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000aq",
      "name": "Append agent_requests",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        2420,
        660
      ],
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $env.MS_SPREADSHEET_ID || \"PASTE_SPREADSHEET_ID\" }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "agent_requests",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "id": "a9000000-0000-0000-0000-0000000000fs",
      "name": "Final Summary Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2640,
        660
      ],
      "parameters": {
        "jsCode": "// Final run summary. No external calls.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Avito Connector Config').first().json;\nconst recs=$('Deduplicate Listings').all().map(i=>i.json);\nconst actor_items_received=recs.length;\nconst invalid_items=recs.filter(r=>str(r.dedup_status)==='invalid').length;\nconst structurally_valid_items=actor_items_received-invalid_items;\nconst hard_skipped_items=recs.filter(r=>str(r.dedup_status)==='hard_skipped').length;\n// Obj3: a structurally valid SEARCH CARD is NOT a confirmed business offer. Separate the categories\n// explicitly; search cards are source candidates pending detail enrichment, never offer/price evidence.\nfunction _active(r){return ['invalid','over_pipeline_limit','hard_skipped'].indexOf(str(r.dedup_status))<0;}\nconst _act=recs.filter(_active);\nconst source_candidate_items=_act.filter(function(r){return str(r.record_type_hint)==='source_candidate'||str(r.touchpoint_type)==='search_card'||r.detail_fetch_required===true;}).length;\nconst confirmed_detail_offer_items=_act.filter(function(r){return str(r.record_type_hint)==='competitor_activity'&&r.detail_fetch_required!==true&&r.is_detail===true;}).length;\nconst irrelevant_items=_act.filter(function(r){return ['irrelevant','irrelevant_live_false_positive','irrelevant_source'].indexOf(str(r.record_type_hint))>=0;}).length;\nconst report_candidate_items=_act.filter(function(r){return str(r.dedup_status)==='unique'&&r.detail_fetch_required!==true&&['irrelevant','irrelevant_live_false_positive','irrelevant_source'].indexOf(str(r.record_type_hint))<0;}).length;\nconst business_relevant_items=confirmed_detail_offer_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 skipped=recs.filter(r=>str(r.predicted_route)==='skipped_log'&&['invalid','over_pipeline_limit','hard_skipped'].indexOf(str(r.dedup_status))<0).length;\nconst predicted_route_counts={};const record_type_counts={};\nfor(const r of recs){\n  if(['invalid','over_pipeline_limit','hard_skipped'].indexOf(str(r.dedup_status))>=0)continue;\n  const pr=str(r.predicted_route)||'unknown';predicted_route_counts[pr]=(predicted_route_counts[pr]||0)+1;\n  const t=str(r.record_type_hint)||'unknown';record_type_counts[t]=(record_type_counts[t]||0)+1;\n}\nconst writeDupAudit=(cfg.write_duplicate_audit===true||str(cfg.write_duplicate_audit).toLowerCase()==='true');\nconst rawWritten=writeDupAudit?(unique+duplicate):unique;\nconst fixtureMode=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nreturn [{ json: {\n  workflow:'09 - Avito Classifieds Listing Connector',\n  agent_request_id:str(cfg.agent_request_id),\n  run_id:str(cfg.run_id),\n  fixture_mode:fixtureMode,\n  live_mode:(cfg.live_mode===true||str(cfg.live_mode).toLowerCase()==='true'),\n  actor_items_received:actor_items_received,\n  structurally_valid_items:structurally_valid_items,\n  invalid_items:invalid_items,\n  business_relevant_items:business_relevant_items,\n  source_candidate_items:source_candidate_items,\n  confirmed_detail_offer_items:confirmed_detail_offer_items,\n  report_candidate_items:report_candidate_items,\n  irrelevant_items:irrelevant_items,\n  hard_skipped_items:hard_skipped_items,\n  over_pipeline_limit:over_pipeline_limit,\n  unique_count:unique,\n  duplicate_count:duplicate,\n  skipped_count:skipped,\n  raw_market_records_written:rawWritten,\n  registry_rows_written:unique,\n  predicted_route_counts:predicted_route_counts,\n  record_type_counts:record_type_counts,\n  next_action:(confirmed_detail_offer_items>0?'Inspect raw_market_records, then run Workflow 08 (Touchpoint Analyzer) manually with agent_request_id_filter. No auto-handoff.':(source_candidate_items>0?'Detail enrichment required; do not run WF08. Only search cards / source candidates (no confirmed detail offers); run WF16 to confirm quarantine. Do NOT treat as competitor offers.':(business_relevant_items>0?'All business-relevant items were duplicates; registry unchanged \u2014 no Workflow 08 run needed.':'No business-relevant unique rows: do NOT run Workflow 08; inspect Apify output / relevance filter (hard_skipped_items shows filtered false positives).')))\n}}];"
      }
    },
    {
      "id": "wf09-build-runlog",
      "name": "Build live_source_runs Row",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2200,
        900
      ],
      "parameters": {
        "jsCode": "function moscowIsoNow(){var m=new Date(Date.now()+10800000);return m.toISOString().replace('Z','+03:00');}\n// WF09 live_source_runs ledger (23 cols, DEC-126). Avito connector; fixture=$0/no calls, live=Apify.\nfunction str(v){return v==null?'':String(v).trim();}\nconst cfg=$('Set Avito Connector Config').first().json||{};\nconst recs=$('Deduplicate Listings').all().map(i=>i.json);\nconst fixtureMode=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nconst received=recs.length;\nconst invalid=recs.filter(r=>str(r.dedup_status)==='invalid').length;\nconst hard=recs.filter(r=>str(r.dedup_status)==='hard_skipped').length;\nconst valid=received-invalid;\nconst relevant=valid-hard;\n// \u00a76 items_relevant counts only CONFIRMED detail offers \u2014 a search card is never a confirmed relevant offer.\nconst confirmedOffers=recs.filter(function(x){return String(x.record_type_hint)==='competitor_activity'&&(x.is_detail===true||String(x.is_detail).toLowerCase()==='true')&&x.detail_fetch_required!==true&&['invalid','over_pipeline_limit','hard_skipped'].indexOf(String(x.dedup_status))<0;}).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 queries=Array.isArray(cfg.search_queries)?cfg.search_queries.join('; '):str(cfg.query);\n// Obj1: approval_token_used is taken from the live safety gate (yes only when it validated the token); value never stored.\nvar approvalTokenUsed='no';\ntry{ var _gate=$('LIVE Apify Safety Gate').first().json; if(_gate&&str(_gate.approval_token_used))approvalTokenUsed=str(_gate.approval_token_used); }catch(e){}\nconst externalCalls=(fixtureMode?0:1);\nconst llmCalls=0;\n// \u00a712 cost: a paid external call whose provider cost was NOT recovered is 'unknown' (never 0 / not_applicable).\nconst sourceCostStatus=(externalCalls>0?'unknown':'not_applicable');\nconst llmCostStatus=(llmCalls>0?'unknown':'not_applicable');\nconst costStatus=((sourceCostStatus==='unknown'||llmCostStatus==='unknown')?'unknown':'not_applicable');\nreturn [{ json: {\n  run_id:str(cfg.run_id)||('avito_'+Date.now()),\n  source_run_id:str(cfg.run_id),\n  agent_request_id:str(cfg.agent_request_id),\n  created_at:moscowIsoNow(),\n  workflow:'WF09 - Avito Classifieds Connector',\n  source_family:'classifieds',\n  platform:'avito',\n  mode:(fixtureMode?'fixture':'live'),\n  approval_token_used:approvalTokenUsed,\n  source_allowlist:queries,\n  max_items:Number(cfg.pipeline_limit)||Number(cfg.live_max_items)||10,\n  items_received:received,\n  items_relevant:confirmedOffers,\n  items_written_raw:unique+dup,\n  items_written:unique+dup,\n  items_unique:unique,\n  items_duplicate:dup,\n  hard_skipped:hard,\n  external_calls:externalCalls,\n  estimated_source_cost_usd:0,\n  actual_source_cost_usd:null,\n  source_cost_status:sourceCostStatus,\n  llm_calls:llmCalls,\n  estimated_llm_cost_usd:0,\n  actual_llm_cost_usd:null,\n  llm_cost_status:llmCostStatus,\n  cost_status:costStatus,\n  status:'completed',\n  error_summary:(invalid>0?('invalid_items='+invalid+' (inspect Apify actor schema)'):''),\n  operator_next_action:(unique>0?'Run WF08 on collected raw records':'No unique business-relevant rows; inspect relevance/Apify output'),\n  notes:'Avito '+(fixtureMode?'fixture':'live Apify')+' run; '+(fixtureMode?'$0, no external calls':'cost not recovered (apify_avito)')+'.'\n}}];"
      }
    },
    {
      "id": "wf09-app-runlog",
      "name": "Append live_source_runs",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        2420,
        900
      ],
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "={{ $env.MS_SPREADSHEET_ID || \"PASTE_SPREADSHEET_ID\" }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "live_source_runs",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// \u00a78.1 LIVE Apify Safety Gate. The paid Apify path is BLOCKED unless ALL conditions hold:\n//   fixture_mode=false AND live_mode=true AND approval token matches AND max_items>0 AND a budget guard is set.\n// NEVER logs the token value \u2014 only approval_token_used=yes/no. If live_mode=false, no Apify call can happen.\nfunction str(v){return v==null?'':String(v).trim();}\nvar cfg=$('Set Avito Connector Config').first().json;\nvar fixture=(cfg.fixture_mode===true||str(cfg.fixture_mode).toLowerCase()==='true');\nvar live=(cfg.live_mode===true||str(cfg.live_mode).toLowerCase()==='true');\nvar tokenProvided=str(cfg.approval_token)!=='';\nvar tokenOk=(!cfg.require_approval_token)|| (str(cfg.approval_token)===str(cfg.expected_approval_token) && str(cfg.expected_approval_token)!=='');\nvar maxItems=Number(cfg.live_max_items||cfg.max_items)||0;\nvar budget=Number(cfg.max_budget_usd)||0;\nvar approval_token_used=tokenProvided?'yes':'no'; // value is NEVER emitted\nif(fixture){ throw new Error('WF09 safety gate: fixture_mode=true \u2014 this branch must not reach the paid Apify actor. Use the fixture path.'); }\nif(!live){ throw new Error('WF09 safety gate: live_mode=false \u2014 paid Apify call BLOCKED (approval_token_used='+approval_token_used+').'); }\nif(!tokenOk){ throw new Error('WF09 safety gate: approval token missing/mismatch \u2014 paid Apify call BLOCKED (approval_token_used='+approval_token_used+'). Set approval_token to the approved value; the value is never logged.'); }\nif(maxItems<=0){ throw new Error('WF09 safety gate: live_max_items must be > 0.'); }\nif(budget<=0){ throw new Error('WF09 safety gate: max_budget_usd cost guard not set (>0 required) \u2014 paid Apify call BLOCKED.'); }\nvar safeCfg=Object.assign({}, cfg); delete safeCfg.approval_token; delete safeCfg.expected_approval_token; // never propagate the token value\nreturn [{ json: Object.assign(safeCfg, { _safety_gate:'passed', approval_token_used:approval_token_used, max_items_effective:maxItems, max_budget_usd:budget }) }];\n"
      },
      "id": "live_apify_safety_gate",
      "name": "LIVE Apify Safety Gate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        420
      ]
    },
    {
      "parameters": {
        "inputSource": "workflowInputs",
        "workflowInputs": {
          "values": [
            {
              "name": "agent_request_id",
              "type": "string"
            },
            {
              "name": "source_run_id",
              "type": "string"
            },
            {
              "name": "workflow_run_id",
              "type": "string"
            },
            {
              "name": "data_mode",
              "type": "string"
            },
            {
              "name": "search_queries",
              "type": "string"
            },
            {
              "name": "max_items",
              "type": "string"
            },
            {
              "name": "approval_token",
              "type": "string"
            },
            {
              "name": "max_budget_usd",
              "type": "string"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        -1160,
        480
      ],
      "id": "wf09-subtrig",
      "name": "When Called by Agent"
    },
    {
      "id": "a9000000-0000-0000-0000-000000anyrow",
      "name": "Any Rows To Write?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1850,
        380
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "cond-anyrow",
              "leftValue": "={{ $json.ms_no_rows === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "a9000000-0000-0000-0000-000000legacy",
      "name": "Write Legacy Request Row?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2290,
        520
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "cond-legacy",
              "leftValue": "={{ $json.ms_agent_mode === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    }
  ],
  "connections": {
    "Manual Start": {
      "main": [
        [
          {
            "node": "Set Avito Connector Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Avito Connector Config": {
      "main": [
        [
          {
            "node": "IF fixture_mode?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF fixture_mode?": {
      "main": [
        [
          {
            "node": "Build Fixture Avito Listings",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "LIVE Apify Safety Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Fixture Avito Listings": {
      "main": [
        [
          {
            "node": "Normalize Avito Listings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apify Avito Classifieds Actor Request": {
      "main": [
        [
          {
            "node": "Normalize Avito Listings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Avito Listings": {
      "main": [
        [
          {
            "node": "Read market_record_registry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read market_record_registry": {
      "main": [
        [
          {
            "node": "Deduplicate Listings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deduplicate Listings": {
      "main": [
        [
          {
            "node": "Build raw_market_records Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build raw_market_records Rows": {
      "main": [
        [
          {
            "node": "Any Rows To Write?",
            "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": "Write Legacy Request Row?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append agent_requests": {
      "main": [
        [
          {
            "node": "Final Summary Output",
            "type": "main",
            "index": 0
          },
          {
            "node": "Build live_source_runs Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build live_source_runs Row": {
      "main": [
        [
          {
            "node": "Append live_source_runs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LIVE Apify Safety Gate": {
      "main": [
        [
          {
            "node": "Apify Avito Classifieds Actor Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Called by Agent": {
      "main": [
        [
          {
            "node": "Set Avito Connector Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Any Rows To Write?": {
      "main": [
        [
          {
            "node": "Append raw_market_records",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build agent_requests Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Legacy Request Row?": {
      "main": [
        [
          {
            "node": "Append agent_requests",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build live_source_runs Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a9000000-avito-classifieds-listing-connector-v006-canonical-url-sandbox-safe-20260611"
}