{
  "id": "ZarifTicketLab01",
  "name": "Zarif ticket triage \u2014 local fixtures",
  "active": false,
  "nodes": [
    {
      "id": "manual",
      "name": "Manual test",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "parameters": {}
    },
    {
      "id": "2",
      "name": "Synthetic events",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Eight synthetic inputs. No customer records or provider calls.\nreturn [\n  {\n    \"event_id\": \"evt-1\",\n    \"ticket_id\": \"DEMO-42\",\n    \"expected_version\": 3\n  },\n  {\n    \"event_id\": \"evt-1\",\n    \"ticket_id\": \"DEMO-42\",\n    \"expected_version\": 3\n  },\n  {\n    \"event_id\": \"evt-2\",\n    \"ticket_id\": \"MISSING\",\n    \"expected_version\": 3\n  },\n  {\n    \"event_id\": \"evt-3\",\n    \"ticket_id\": \"DEMO-42\",\n    \"expected_version\": 3,\n    \"fixture\": \"invalid-label\"\n  },\n  {\n    \"event_id\": \"evt-4\",\n    \"ticket_id\": \"DEMO-42\",\n    \"expected_version\": \"3\"\n  },\n  {\n    \"event_id\": \"evt-5\",\n    \"ticket_id\": \"DEMO-43\",\n    \"expected_version\": 3\n  },\n  {\n    \"event_id\": \"evt-6\",\n    \"ticket_id\": \"DEMO-42\",\n    \"expected_version\": 3,\n    \"fixture\": \"malformed-json\"\n  },\n  {\n    \"event_id\": \"evt-7\",\n    \"ticket_id\": \"DEMO-42\",\n    \"expected_version\": 2\n  }\n].map(json => ({json}));"
      }
    },
    {
      "id": "3",
      "name": "Validate events",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const seen = new Set();\nreturn $input.all().map((item, index) => {\n  const row = item.json;\n  let reason = null;\n  if (typeof row.event_id !== 'string' || !row.event_id || typeof row.ticket_id !== 'string' || !Number.isInteger(row.expected_version) || row.expected_version < 1) reason = 'invalid_event';\n  else if (seen.has(row.event_id)) reason = 'duplicate_event_in_batch';\n  else seen.add(row.event_id);\n  return {json: {...row, event_valid: reason === null, reason}, pairedItem: {item: index}};\n});"
      }
    },
    {
      "id": "4",
      "name": "Event valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        720,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "Event valid?-condition",
              "leftValue": "={{ $json.event_valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "5",
      "name": "Lookup and model fixture",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        960,
        -100
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// In production these are separate database lookup and model nodes.\n// This fixture simulates both without credentials or external requests.\nconst tickets = {'DEMO-42': {version: 3, label: 'billing'}, 'DEMO-43': {version: 3, label: 'technical'}};\nreturn $input.all().map((item, index) => {\n  const ticket = tickets[item.json.ticket_id];\n  const raw = item.json.fixture === 'malformed-json' ? 'not JSON' : JSON.stringify({label: item.json.fixture === 'invalid-label' ? 'delete-account' : ticket?.label});\n  return {json: {...item.json, found: Boolean(ticket), stored_version: ticket?.version ?? null, model_output: raw}, pairedItem: {item: index}};\n});"
      }
    },
    {
      "id": "6",
      "name": "Validate proposals",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        -100
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "return $input.all().map((item, index) => {\n  const row = item.json;\n  let reason = null, label = null;\n  if (!row.found) reason = 'ticket_not_found';\n  else if (row.expected_version !== row.stored_version) reason = 'stale_version';\n  else {\n    try {\n      const proposal = JSON.parse(row.model_output);\n      if (!proposal || typeof proposal !== 'object' || Array.isArray(proposal) || Object.keys(proposal).length !== 1 || !['billing','technical','other'].includes(proposal.label)) reason = 'invalid_label_contract';\n      else label = proposal.label;\n    } catch { reason = 'invalid_json'; }\n  }\n  return {json: {...row, proposal_valid: reason === null, reason, label}, pairedItem: {item: index}};\n});"
      }
    },
    {
      "id": "7",
      "name": "Proposal valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1440,
        -100
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "Proposal valid?-condition",
              "leftValue": "={{ $json.proposal_valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "8",
      "name": "Review items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1680,
        -200
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "return $input.all().map((item, index) => ({\n  json: {event_id: item.json.event_id, ticket_id: item.json.ticket_id, expected_version: item.json.stored_version, proposed_label: item.json.label, state: 'awaiting_review', applied: false},\n  pairedItem: {item: index}\n}));"
      }
    },
    {
      "id": "9",
      "name": "Rejected proposals",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1680,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "return $input.all().map((item, index) => ({json: {event_id: item.json.event_id, ticket_id: item.json.ticket_id, state: 'rejected', reason: item.json.reason, applied: false}, pairedItem: {item: index}}));"
      }
    },
    {
      "id": "10",
      "name": "Rejected events",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        960,
        180
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "return $input.all().map((item, index) => ({json: {event_id: item.json.event_id, ticket_id: item.json.ticket_id, state: 'rejected', reason: item.json.reason, applied: false}, pairedItem: {item: index}}));"
      }
    }
  ],
  "connections": {
    "Manual test": {
      "main": [
        [
          {
            "node": "Synthetic events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Synthetic events": {
      "main": [
        [
          {
            "node": "Validate events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate events": {
      "main": [
        [
          {
            "node": "Event valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Event valid?": {
      "main": [
        [
          {
            "node": "Lookup and model fixture",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Rejected events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup and model fixture": {
      "main": [
        [
          {
            "node": "Validate proposals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate proposals": {
      "main": [
        [
          {
            "node": "Proposal valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Proposal valid?": {
      "main": [
        [
          {
            "node": "Review items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Rejected proposals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "tags": []
}
