{
  "name": "15 - Live Source Run Logger (Manual)",
  "nodes": [
    {
      "parameters": {
        "content": "## WF15 \u2014 Live Source Run Logger v0.1 (DEC-126)\n\n**\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435:** \u0435\u0434\u0438\u043d\u044b\u0439 \u0436\u0443\u0440\u043d\u0430\u043b \u043f\u0440\u043e\u0433\u043e\u043d\u043e\u0432 live-\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b\u0445 \u0432\u043e\u0440\u043a\u0444\u043b\u043e\u0443 \u2014 \u0432\u043a\u043b\u0430\u0434\u043a\u0430 live_source_runs (23 \u043a\u043e\u043b\u043e\u043d\u043a\u0438, docs/TABLE_SCHEMA.md). WF11/WF12/WF13 \u043f\u0438\u0448\u0443\u0442 \u0432 \u043d\u0435\u0451 \u0410\u0412\u0422\u041e\u041c\u0410\u0422\u0418\u0427\u0415\u0421\u041a\u0418 (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u043d\u043e\u0434\u044b). WF15 \u2014 \u0440\u0443\u0447\u043d\u043e\u0439 \u043b\u043e\u0433\u0433\u0435\u0440 \u0434\u043b\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432: \u043f\u0440\u043e\u0433\u043e\u043d\u044b WF09 (Avito live), \u0438\u0441\u0442\u043e\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0431\u044d\u043a\u0444\u0438\u043b\u043b, \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 guard-\u043e\u043c \u043f\u043e\u043f\u044b\u0442\u043a\u0438, \u043f\u0440\u043e\u0433\u043e\u043d\u044b WF04 \u043f\u043e \u0441\u0430\u0439\u0442\u0430\u043c.\n\n\u041a\u0430\u0436\u0434\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442: \u0447\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u043e\u0441\u044c, \u043a\u0430\u043a\u043e\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u043e\u0438\u043b\u043e, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0431\u0440\u0430\u043d\u043e, \u0431\u044b\u043b \u043b\u0438 \u043f\u0440\u043e\u0433\u043e\u043d \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u0433\u0435\u0439\u0442\u043e\u043c, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435.\n\n**\u041f\u0440\u0430\u0432\u0438\u043b\u0430:** $0, \u0431\u0435\u0437 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u0431\u0435\u0437 Claude; \u0442\u043e\u043a\u0435\u043d\u044b \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0438\u044f \u041d\u0415 \u043b\u043e\u0433\u0438\u0440\u0443\u044e\u0442\u0441\u044f (\u0442\u043e\u043b\u044c\u043a\u043e yes/no); active=false; \u0437\u0430\u043f\u0443\u0441\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e; MSK +03:00. \u041f\u0438\u0448\u0435\u0442 \u0422\u041e\u041b\u042c\u041a\u041e live_source_runs.",
        "height": 520,
        "width": 640
      },
      "id": "wf15-note-1",
      "name": "Overview Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -460,
        -300
      ]
    },
    {
      "parameters": {
        "content": "## Setup & Test\n\n1. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u043a\u043b\u0430\u0434\u043a\u0443 live_source_runs (23 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0438\u0437 docs/TABLE_SCHEMA.md). \u041d\u0415 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c.\n2. \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c SPREADSHEET_ID \u0438 \u043f\u0435\u0440\u0435\u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u043a\u0440\u0435\u0434\u0435\u043d\u0448\u043b \u043d\u0430 Append-\u043d\u043e\u0434\u0435.\n3. \u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u043e\u043b\u044f \u0432 Set Run Log Entry (workflow/source_family/platform/mode/\u0441\u0447\u0451\u0442\u0447\u0438\u043a\u0438/\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c) \u043f\u043e\u0434 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043f\u0440\u043e\u0433\u043e\u043d.\n4. Execute once -> live_source_runs +1; Validate-\u043d\u043e\u0434\u0430 \u0431\u0440\u043e\u0441\u0430\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u0443\u044e \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438 \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0445 enum-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445 (mode: fixture/live/deterministic/llm_summary/blocked_by_guard; status: completed/failed/blocked/partial; source_family: classifieds/social_channel/public_discussion/public_lead_source/lead_triage/web_competitor/report_layer/other).\n5. \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u043e\u043d \u0441 \u0442\u0435\u043c\u0438 \u0436\u0435 \u043f\u043e\u043b\u044f\u043c\u0438 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c (append-only \u0436\u0443\u0440\u043d\u0430\u043b) \u2014 run_id \u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c.",
        "height": 520,
        "width": 620
      },
      "id": "wf15-note-2",
      "name": "Setup and Test Note RU",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        220,
        -300
      ]
    },
    {
      "parameters": {},
      "id": "wf15-start",
      "name": "Manual Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -420,
        460
      ]
    },
    {
      "parameters": {
        "jsCode": "// OPERATOR-EDITED run log entry. Fill these fields to log one run of any live-capable workflow\n// (typically WF09 Avito live runs or guard-blocked attempts). $0, no external calls, no Claude.\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());}\nreturn [{ json: {\n  run_id:'manual_log_'+moscowStamp(),        // or the original workflow run_id being logged\n  created_at:moscowIsoNow(),\n  workflow:'WF09 - Avito Classifieds Connector', // which workflow ran\n  source_family:'classifieds',               // classifieds/social_channel/public_discussion/public_lead_source/lead_triage/web_competitor/report_layer/other\n  platform:'avito',\n  mode:'live',                               // fixture/live/deterministic/llm_summary/blocked_by_guard\n  approval_token_used:'yes',                 // yes/no \u2014 NEVER paste the token value itself\n  source_allowlist:'',                       // queries/groups/channels/domains used\n  max_items:10,\n  items_received:0,\n  items_relevant:0,\n  items_written_raw:0,\n  items_unique:0,\n  items_duplicate:0,\n  hard_skipped:0,\n  external_calls:0,\n  estimated_source_cost_usd:0,\n  llm_calls:0,\n  estimated_llm_cost_usd:0,\n  status:'completed',                        // completed/failed/blocked/partial\n  error_summary:'',\n  operator_next_action:'',\n  notes:'manually logged via WF15'\n}}];"
      },
      "id": "wf15-entry",
      "name": "Set Run Log Entry",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -200,
        460
      ]
    },
    {
      "parameters": {
        "jsCode": "// Validate & normalize the run log row: enum checks + numeric coercion, so the live_source_runs tab\n// stays queryable. Throws a clear error instead of writing junk. No external calls.\nfunction str(v){return v==null?'':String(v).trim();}\nfunction num(v){var n=Number(v);return isFinite(n)?n:0;}\nconst MODES=['fixture','live','deterministic','llm_summary','blocked_by_guard'];\nconst STATUSES=['completed','failed','blocked','partial'];\nconst FAMILIES=['classifieds','social_channel','public_discussion','public_lead_source','lead_triage','web_competitor','report_layer','other'];\nconst r=$input.first().json;\nif(!str(r.run_id))throw new Error('WF15: run_id is required');\nif(!str(r.workflow))throw new Error('WF15: workflow is required');\nif(MODES.indexOf(str(r.mode))<0)throw new Error('WF15: mode must be one of '+MODES.join('/'));\nif(STATUSES.indexOf(str(r.status))<0)throw new Error('WF15: status must be one of '+STATUSES.join('/'));\nif(FAMILIES.indexOf(str(r.source_family))<0)throw new Error('WF15: source_family must be one of '+FAMILIES.join('/'));\nif(['yes','no'].indexOf(str(r.approval_token_used))<0)throw new Error('WF15: approval_token_used must be yes/no \u2014 never the token itself');\nif(/I_APPROVE/i.test(JSON.stringify(r)))throw new Error('WF15: approval token VALUE detected in the entry \u2014 remove it; log only yes/no');\nconst NUM=['max_items','items_received','items_relevant','items_written_raw','items_unique','items_duplicate','hard_skipped','external_calls','estimated_source_cost_usd','llm_calls','estimated_llm_cost_usd'];\nconst out=Object.assign({},r);\nfor(const k of NUM)out[k]=num(r[k]);\nreturn [{ json: out }];"
      },
      "id": "wf15-validate",
      "name": "Validate Run Log Row",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        20,
        460
      ]
    },
    {
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "PASTE_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "live_source_runs",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "wf15-append",
      "name": "Append live_source_runs",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        240,
        460
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Final summary. No external calls.\nconst r=$('Validate Run Log Row').first().json;\nreturn [{ json: { workflow:'15 - Live Source Run Logger', logged_run_id:r.run_id, logged_workflow:r.workflow, mode:r.mode, status:r.status, estimated_source_cost_usd:r.estimated_source_cost_usd, estimated_llm_cost_usd:r.estimated_llm_cost_usd, next_action:'Row appended to live_source_runs. Keep COSTS_AND_LIMITS.md in sync for paid runs.' } }];"
      },
      "id": "wf15-final",
      "name": "Final Summary Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        460
      ]
    }
  ],
  "connections": {
    "Manual Start": {
      "main": [
        [
          {
            "node": "Set Run Log Entry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Run Log Entry": {
      "main": [
        [
          {
            "node": "Validate Run Log Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Run Log Row": {
      "main": [
        [
          {
            "node": "Append live_source_runs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append live_source_runs": {
      "main": [
        [
          {
            "node": "Final Summary Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "wf15-v002-lead-source-family-20260617"
}