{
  "name": "TridenGuard_v1",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "tridenguard",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        7456,
        6848
      ],
      "id": "0efec550-69fe-4d2f-b850-ad7366e33517",
      "name": "Frontend Webhook"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "auth-check",
              "leftValue": "={{ $json.headers['x-api-key'] }}",
              "rightValue": "tridenguard_secret_key_2026",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        7664,
        6848
      ],
      "id": "db990e19-e932-4c9b-a23b-173c666d1d40",
      "name": "Auth Gate"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "dpi-check",
              "leftValue": "={{ $json.action }}",
              "rightValue": "ALLOW",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        8544,
        6848
      ],
      "id": "a359efd4-3c2b-428a-a0d3-d32a1b8d4911",
      "name": "DPI Gate"
    },
    {
      "parameters": {
        "model": "phi4-mini:3.8b",
        "options": {
          "temperature": 0,
          "format": "json"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "typeVersion": 1,
      "position": [
        8928,
        6704
      ],
      "id": "fa07814b-6767-48f6-ab2a-b1851a5295c9",
      "name": "Ollama Chat Model1",
      "credentials": {
        "ollamaApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "text": "={{ $(\"Frontend Webhook\").item.json.body.text }}",
        "schemaType": "fromJson",
        "jsonSchemaExample": "{\n  \"axiom\": \"Contractor must complete installation within 30 days at main site under active permit\",\n  \"radicals\": [\n    {\"radical\": \"Actor\", \"value\": \"Contractor\"},\n    {\"radical\": \"Deontic\", \"value\": \"Must (Obligation)\"},\n    {\"radical\": \"Action\", \"value\": \"Complete installation\"},\n    {\"radical\": \"Temporal\", \"value\": \"Within 30 days\"},\n    {\"radical\": \"Spatial\", \"value\": \"Main site\"},\n    {\"radical\": \"Condition\", \"value\": \"Permit is active\"}\n  ]\n}",
        "options": {
          "systemPromptTemplate": "Role: Senior AI Engineer / Legal Ontologist.\n\nTask: Extract foundational facts (axioms) and their atomic components (radicals) from legal or technical text.\n\nAtomic radicals (ontology of 8):\n- Actor: entity explicitly named as performing the action. who.\n- Action: the core activity or verb explicitly stated. does what.\n- Object: the target or entity explicitly affected by the action. to what/whom.\n- Deontic: the nature of the rule (obligation, permission, prohibition). must/may/must not.\n- Condition: prerequisites or triggers explicitly stated. if.\n- Temporal: time constraints, deadlines, periods explicitly stated. when.\n- Spatial: geographic or location constraints explicitly stated. where.\n- Metric: measurable values, thresholds, limits explicitly stated. how much.\n\nMust:\n1. only extract radicals that are explicitly stated in the text.\n2. if no actor is explicitly named, omit the actor radical.\n3. prioritize Action as the core radical. if there is no Action, keep radicals to a minimum.\n4. prefer fewer radicals over inventing one. 2 radicals is better than 4 with one invented.\n5. if a radical type is uncertain, omit it.\n6. every radical object must have exactly two keys: \"radical\" and \"value\".\n7. return valid JSON only. no markdown. no explanation.\n\nMust not:\n1. invent a radical that is not explicitly in the text.\n2. extract Metric from a Temporal expression, or Temporal from a Spatial one.\n3. extract Deontic and Action as the same radical.\n4. use the radical name as a key directly.\n5. pad the output to reach a higher number of radicals.\n\nExamples of correct extraction:\n\nText: \"The contractor must complete the installation within 30 days.\"\nOutput: {\"axiom\": \"Contractor must complete installation within 30 days.\", \"radicals\": [{\"radical\": \"Actor\", \"value\": \"contractor\"}, {\"radical\": \"Deontic\", \"value\": \"must\"}, {\"radical\": \"Action\", \"value\": \"complete the installation\"}, {\"radical\": \"Object\", \"value\": \"the installation\"}, {\"radical\": \"Temporal\", \"value\": \"30 days\"}]}\n\nText: \"The report must be submitted before Friday.\"\nOutput: {\"axiom\": \"The report must be submitted before Friday.\", \"radicals\": [{\"radical\": \"Action\", \"value\": \"submit the report\"}, {\"radical\": \"Object\", \"value\": \"the report\"}, {\"radical\": \"Deontic\", \"value\": \"must\"}, {\"radical\": \"Temporal\", \"value\": \"before Friday\"}]}\n\nText: \"Any act violating the regulation is strictly prohibited.\"\nOutput: {\"axiom\": \"Any act violating the regulation is strictly prohibited.\", \"radicals\": [{\"radical\": \"Deontic\", \"value\": \"prohibited\"}, {\"radical\": \"Object\", \"value\": \"any act violating the regulation\"}]}\n\nText: \"The profitability threshold is set at 15%.\"\nOutput: {\"axiom\": \"The profitability threshold is set at 15%.\", \"radicals\": [{\"radical\": \"Metric\", \"value\": \"15%\"}]}\n\nText: \"Whenever the index exceeds 3.5% for two consecutive quarters.\"\nOutput: {\"axiom\": \"Whenever the index exceeds 3.5% for two consecutive quarters.\", \"radicals\": [{\"radical\": \"Condition\", \"value\": \"whenever the index exceeds 3.5%\"}, {\"radical\": \"Temporal\", \"value\": \"two consecutive quarters\"}]}\n\nOnly output the JSON object. No additional text."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.informationExtractor",
      "typeVersion": 1.2,
      "position": [
        8928,
        6832
      ],
      "id": "00b1515f-a3f4-463f-b652-dc3741d95264",
      "name": "Information Extractor1"
    },
    {
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  const output = item.json.output || {};\n  const radicals = Array.isArray(output.radicals) ? output.radicals : [];\n  const types = new Set(radicals.map(r => r.radical));\n  const original_text = $(\"Frontend Webhook\").item.json.body?.text || \"\";\n\n  // --- Helper: check radical presence ---\n  const has = (r) => types.has(r);\n\n  // --- 8 Atomic Exclusion Rules ---\n  const rules = [\n    { id: 'R1_SUBJECT_WITHOUT_ACTION',   test: () => (has('Actor') || has('Deontic')) && !has('Action') },\n    { id: 'R2_ACTION_WITHOUT_SUBJECT',   test: () => has('Action') && !has('Actor') },\n    { id: 'R3_OBJECT_WITHOUT_REFERENT',  test: () => has('Object') && !has('Actor') && !has('Action') },\n    { id: 'R4_ORPHAN_METRIC',            test: () => has('Metric') && !has('Actor') && !has('Object') },\n    { id: 'R5_CONDITION_WITHOUT_TRIGGER',test: () => has('Condition') && !has('Action') },\n    { id: 'R6_TEMPORAL_WITHOUT_ANCHOR',  test: () => has('Temporal') && !has('Actor') && !has('Action') },\n    { id: 'R7_INERT_SPATIAL',            test: () => has('Spatial') && !has('Actor') && !has('Action') },\n    { id: 'R8_DEONTIC_WITHOUT_BEHAVIOR', test: () => has('Deontic') && !has('Action') },\n  ];\n\n  const errors = rules.filter(r => r.test()).map(r => r.id);\n\n  // --- Empty extraction check ---\n  if (radicals.length === 0) { errors.push('EMPTY_EXTRACTION'); }\n\n  // --- Source Integrity Check ---\n  if (!$input.item.json.source_integrity) { errors.push('UNGROUNDED_RADICAL'); }\n\n  // --- Business rules ---\n  const deonticCount = radicals.filter(r => r.radical === 'Deontic').length;\n  if (deonticCount > 1) { errors.push('DUPLICATE_DEONTIC'); }\n  if (has('Metric') && !has('Action') && !has('Object') && !has('Condition') && !has('Temporal')) {\n    errors.push('METRIC_WITHOUT_CONTEXT');\n  }\n\n  const is_valid = errors.length === 0 && radicals.length >= 2 && output.axiom?.length > 5;\n\n  item.json = {\n    source_integrity: $input.item.json.source_integrity,\n    ungrounded_radicals: JSON.stringify($input.item.json.ungrounded_radicals || []),\n    case_id: Math.random().toString(36).substring(2, 15),\n    timestamp: new Date().toISOString(),\n    status: is_valid ? 'VALIDATED' : 'QUARANTINED',\n    rejection_reason: errors.length > 0 ? errors.join(', ') : 'NONE',\n    and_gate_ok: is_valid,\n    radicals_extracted: radicals.length,\n    axiom: output.axiom,\n    radicals_json: JSON.stringify(radicals),\n    original_text\n  };\n}\nreturn $input.all();"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        10016,
        6832
      ],
      "id": "d09299b6-0c7e-44b9-84b2-57e1850ea5d6",
      "name": "Deterministic Validator"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\n  \"error\": \"Unauthorized\",\n  \"message\": \"Invalid or missing x-api-key header.\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        7872,
        7056
      ],
      "id": "20d63ca0-3669-4aae-8737-f6f0bb4658e3",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\n  \"error\": \"Security Violation\",\n  \"action\": \"DENY\",\n  \"reason\": \"Prompt blocked by Lobster Trap DPI\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        9024,
        7152
      ],
      "id": "1aae4f66-a3f9-427b-b9f5-c3859faf5bb1",
      "name": "Respond to Webhook1"
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "value": "ZIfqSQ6eFCU5nl3v",
          "mode": "list",
          "cachedResultName": "audit-log",
          "cachedResultUrl": "/projects/CfdxAVwXqndjJVk6/datatables/ZIfqSQ6eFCU5nl3v"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {
            "and_gate_ok": false,
            "source_integrity": false,
            "has_critical": false,
            "has_warning": false,
            "has_unknown": false,
            "has_validates": false,
            "has_borderline": false
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "case_id",
              "displayName": "case_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rejection_reason",
              "displayName": "rejection_reason",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "and_gate_ok",
              "displayName": "and_gate_ok",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_extracted",
              "displayName": "radicals_extracted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "axiom",
              "displayName": "axiom",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_json",
              "displayName": "radicals_json",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "original_text",
              "displayName": "original_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "trident_status",
              "displayName": "trident_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "source_integrity",
              "displayName": "source_integrity",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "ungrounded_radicals",
              "displayName": "ungrounded_radicals",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_critical",
              "displayName": "has_critical",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_warning",
              "displayName": "has_warning",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_unknown",
              "displayName": "has_unknown",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_validates",
              "displayName": "has_validates",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "error_count",
              "displayName": "error_count",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "errors",
              "displayName": "errors",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_borderline",
              "displayName": "has_borderline",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        11104,
        6752
      ],
      "id": "9ad06ea1-fdaa-46ea-bcb5-c1fdaa60c4bf",
      "name": "Save to Audit Table"
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "value": "i465juhY0rXHhLja",
          "mode": "list",
          "cachedResultName": "quarantine-log",
          "cachedResultUrl": "/projects/CfdxAVwXqndjJVk6/datatables/i465juhY0rXHhLja"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {
            "and_gate_ok": false,
            "radicals_extracted": 0
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "case_id",
              "displayName": "case_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rejection_reason",
              "displayName": "rejection_reason",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "and_gate_ok",
              "displayName": "and_gate_ok",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_extracted",
              "displayName": "radicals_extracted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "axiom",
              "displayName": "axiom",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_json",
              "displayName": "radicals_json",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "original_text",
              "displayName": "original_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "trident_status",
              "displayName": "trident_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        11104,
        6960
      ],
      "id": "db4ecaf1-2cf6-4e8d-9467-43cc431ad7e9",
      "name": "Save to Quarantine Table"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:8080/inspect",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={\"text\": \"{{ $json.body.text }}\"}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        8080,
        6848
      ],
      "id": "bb20e06e-6c65-4d38-bb0f-a8b883b3a8c7",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "jsCode": "const output = $input.item.json.output || {};\nlet radicals = output.radicals || [];\n\nradicals = radicals.map(r => {\n  if (r.radical && r.value) return r;\n  \n  const validRadicals = ['Actor', 'Deontic', 'Action', 'Object', 'Temporal', 'Spatial', 'Metric', 'Condition'];\n  for (const key of validRadicals) {\n    if (r[key]) {\n      return { radical: key, value: r[key] };\n    }\n  }\n  return null;\n}).filter(r => r !== null);\n\nreturn {\n  json: {\n    output: {\n      axiom: output.axiom || '',\n      radicals: radicals\n    }\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        9232,
        6832
      ],
      "id": "27a090d5-b7a9-4bf9-8932-62103ee4f7ea",
      "name": "Normalizer"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 3
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.has_validated }}",
                    "rightValue": "true",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "b920ff96-85a5-49ba-afca-6c8e8e5babf6"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "VALIDATED"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "33f43b45-c472-4cbf-919f-a610c2a89eb0",
                    "leftValue": "={{ $json.has_critical }}",
                    "rightValue": "true",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "CRITICAL"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "939aa15c-1333-43cc-9b1d-91ad030030e0",
                    "leftValue": "={{ $json.has_warning }}",
                    "rightValue": "true",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "WARNING"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "25e2a694-deea-4f7e-85d6-471cb6550b78",
                    "leftValue": "={{ $json.has_unknown }}",
                    "rightValue": "true",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "UNKNOWN"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "a1a8f6dd-05cc-421f-9d7f-1cd1c3caeee6",
                    "leftValue": "={{ $json.rejection_reason }}",
                    "rightValue": "UNGROUNDED_RADICAL",
                    "operator": {
                      "type": "string",
                      "operation": "regex"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "UNGROUNDED"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "a26dfa11-aa17-4834-a6fe-18eafab92acb",
                    "leftValue": "={{ $json.has_borderline }}",
                    "rightValue": "true",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "BORDERLINE"
            }
          ]
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.4,
      "position": [
        10880,
        6768
      ],
      "id": "17778f66-cf95-4cdc-9246-7951f11dfa94",
      "name": "Switch"
    },
    {
      "parameters": {
        "jsCode": "const text = $input.item.json.body.text || \"\";\n\nreturn {\n  json: {\n    ...$input.item.json,\n    source_truth: text,\n    source_text_length: text.length,\n    source_hash: Buffer.from(text).toString('base64').substring(0, 32)\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        7872,
        6848
      ],
      "id": "fa132ad6-f7ad-4e8c-aa0d-47e3dc84b11b",
      "name": "Source Truth Anchor"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "discard",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        8816,
        7504
      ],
      "id": "e727942b-4ff1-4f35-9061-d1d0db15ca61",
      "name": "Webhook"
    },
    {
      "parameters": {
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "value": "i465juhY0rXHhLja",
          "mode": "list",
          "cachedResultName": "quarantine-log",
          "cachedResultUrl": "/projects/CfdxAVwXqndjJVk6/datatables/i465juhY0rXHhLja"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "case_id",
              "keyValue": "={{ $json.body.caseId }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "and_gate_ok": false,
            "source_integrity": false,
            "status": "DISCARDED",
            "radicals_extracted": 0
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "case_id",
              "displayName": "case_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rejection_reason",
              "displayName": "rejection_reason",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "and_gate_ok",
              "displayName": "and_gate_ok",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_extracted",
              "displayName": "radicals_extracted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "axiom",
              "displayName": "axiom",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_json",
              "displayName": "radicals_json",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "original_text",
              "displayName": "original_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "trident_status",
              "displayName": "trident_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "source_integrity",
              "displayName": "source_integrity",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "ungrounded_radicals",
              "displayName": "ungrounded_radicals",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        9024,
        7504
      ],
      "id": "5f858988-5a9c-48f2-883f-d02264961fd9",
      "name": "Update row(s)"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"ok\": true, \"action\": \"discarded\", \"case_id\": \"{{ $json.body.caseId }}\"}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        9232,
        7504
      ],
      "id": "e3474ed9-f5a1-4e24-8c59-1b8cef70fbd6",
      "name": "Respond to Webhook3"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "approve",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        7808,
        7520
      ],
      "id": "1cfbcfd1-d9e8-430e-bb5c-7bb43c4a494d",
      "name": "Webhook1"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"ok\": true, \"action\": \"approved\", \"case_id\": \"{{ $json.body.caseId }}\"}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        8224,
        7520
      ],
      "id": "7fe1c9af-79e6-4879-90bc-370365819214",
      "name": "Respond to Webhook4"
    },
    {
      "parameters": {
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "value": "i465juhY0rXHhLja",
          "mode": "list",
          "cachedResultName": "quarantine-log",
          "cachedResultUrl": "/projects/CfdxAVwXqndjJVk6/datatables/i465juhY0rXHhLja"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "case_id",
              "keyValue": "={{ $json.body.caseId }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "and_gate_ok": false,
            "source_integrity": false,
            "status": "APPROVED",
            "radicals_extracted": 0
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "case_id",
              "displayName": "case_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rejection_reason",
              "displayName": "rejection_reason",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "and_gate_ok",
              "displayName": "and_gate_ok",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_extracted",
              "displayName": "radicals_extracted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "axiom",
              "displayName": "axiom",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_json",
              "displayName": "radicals_json",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "original_text",
              "displayName": "original_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "trident_status",
              "displayName": "trident_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "source_integrity",
              "displayName": "source_integrity",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "ungrounded_radicals",
              "displayName": "ungrounded_radicals",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        8016,
        7520
      ],
      "id": "229425b7-d380-4bb5-99a2-11d0bf4dfa57",
      "name": "Update row(s)1"
    },
    {
      "parameters": {
        "jsCode": "const output = $input.item.json || {};\nconst errors = (output.rejection_reason && output.rejection_reason !== 'NONE') ? output.rejection_reason.split(', ') : [];\n\nconst has_critical = errors.some(e => \n  e.startsWith('UNGROUNDED_RADICAL') || \n  e.includes('R1') || e.includes('R2') || e.includes('R3') || e.includes('R4') || e.includes('R8') ||\n  e === 'EMPTY_EXTRACTION'\n);\nconst has_warning = errors.some(e => e.includes('R5') || e.includes('R6') || e.includes('R7'));\nconst has_borderline = !has_critical && !has_warning && errors.length > 0 && output.source_integrity;\nconst has_unknown = !has_critical && !has_warning && !has_borderline && errors.length > 0;\nconst has_validated = errors.length === 0 && output.source_integrity;\n\nreturn {\n  json: {\n    ...output,\n    has_critical,\n    has_warning,\n    has_borderline,\n    has_unknown,\n    has_validated,\n    error_count: errors.length\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        10384,
        6832
      ],
      "id": "8f424e1a-29fd-4a46-93ee-d29fbffba204",
      "name": "Semantic Rule Engine"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"case_id\": \"{{ $json.case_id }}\", \"status\": \"VALIDATED\"}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        11312,
        6752
      ],
      "id": "64eefef7-2e92-494e-9d7c-0908a00b13b2",
      "name": "Respond to Webhook (Audit)"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "{\"case_id\": \"{{ $json.case_id }}\", \"status\": \"{{ $json.status }}\", \"rejection_reason\": \"{{ $json.rejection_reason }}\", \"severity\": \"{{ $json.has_critical ? 'CRITICAL' : ($json.has_warning ? 'WARNING' : 'UNKNOWN') }}\"}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        11312,
        6960
      ],
      "id": "3f8a500f-7e2d-475a-99a3-1e8e352caf81",
      "name": "Respond to Webhook (Quarantine)"
    },
    {
      "parameters": {
        "jsCode": "const output = $input.item.json.output || {};\nconst radicals = output.radicals || [];\nconst originalText = $input.item.json.original_text || $(\"Frontend Webhook\").item.json.body?.text || \"\";\nconst textLower = originalText.toLowerCase();\n\nlet allGrounded = true;\nconst ungrounded = [];\n\nfor (const r of radicals) {\n    const value = (r.value || \"\").toLowerCase();\n    // Busca si el valor o una parte significativa del mismo aparece en el texto fuente\n    if (value.length > 3 && !textLower.includes(value.substring(0, 4))) {\n        allGrounded = false;\n        ungrounded.push({ radical: r.radical, claimed: r.value });\n    }\n}\n\nreturn {\n    json: {\n        ...$input.item.json,\n        source_integrity: allGrounded,\n        ungrounded_radicals: ungrounded\n    }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        9696,
        6832
      ],
      "id": "7867c861-415a-4036-8c9d-ce504e768bba",
      "name": "Radical Grounding Check"
    },
    {
      "parameters": {
        "jsCode": "const text = $('Source Truth Anchor').item.json.source_truth || 'N/A';\n\nreturn {\n  json: {\n    case_id: Math.random().toString(36).substring(2, 15),\n    timestamp: new Date().toISOString(),\n    status: 'BLOCKED',\n    rejection_reason: $json.reason || 'DPI_BLOCKED',\n    source_integrity: true,\n    has_critical: true,\n    has_warning: false,\n    has_borderline: false,\n    has_unknown: false,\n    error_count: 1,\n    radicals_extracted: 0,\n    original_text: text\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        8656,
        7152
      ],
      "id": "9973eeaf-cbf3-43e9-9ede-838e5b13c6c9",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "value": "IJCUXxKxD8PhrlWF",
          "mode": "list",
          "cachedResultName": "dpi-block-log",
          "cachedResultUrl": "/projects/CfdxAVwXqndjJVk6/datatables/IJCUXxKxD8PhrlWF"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "source_integrity": "={{ $json.source_integrity }}",
            "has_critical": "={{ $json.has_critical }}",
            "has_warning": "={{ $json.has_warning }}",
            "has_borderline": "={{ $json.has_borderline }}",
            "has_unknown": "={{ $json.has_unknown }}",
            "case_id": "={{ $json.case_id }}",
            "timestamp": "={{ $json.timestamp }}",
            "status": "={{ $json.status }}",
            "rejection_reason": "={{ $json.rejection_reason }}",
            "error_count": "={{ $json.error_count }}",
            "radicals_extracted": "={{ $json.radicals_extracted }}",
            "original_text": "={{ $json.original_text }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "case_id",
              "displayName": "case_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "rejection_reason",
              "displayName": "rejection_reason",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "source_integrity",
              "displayName": "source_integrity",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_critical",
              "displayName": "has_critical",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_warning",
              "displayName": "has_warning",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_borderline",
              "displayName": "has_borderline",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "has_unknown",
              "displayName": "has_unknown",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "boolean",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "error_count",
              "displayName": "error_count",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "radicals_extracted",
              "displayName": "radicals_extracted",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "readOnly": false,
              "removed": false
            },
            {
              "id": "original_text",
              "displayName": "original_text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "readOnly": false,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        8864,
        7152
      ],
      "id": "bd41c8df-b6d4-4d0e-9017-f8c4e25b0480",
      "name": "Save to dpi-block-log"
    }
  ],
  "connections": {
    "Frontend Webhook": {
      "main": [
        [
          {
            "node": "Auth Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DPI Gate": {
      "main": [
        [
          {
            "node": "Information Extractor1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Information Extractor1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Information Extractor1": {
      "main": [
        [
          {
            "node": "Normalizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deterministic Validator": {
      "main": [
        [
          {
            "node": "Semantic Rule Engine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Auth Gate": {
      "main": [
        [
          {
            "node": "Source Truth Anchor",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to Audit Table": {
      "main": [
        [
          {
            "node": "Respond to Webhook (Audit)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to Quarantine Table": {
      "main": [
        [
          {
            "node": "Respond to Webhook (Quarantine)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "DPI Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalizer": {
      "main": [
        [
          {
            "node": "Radical Grounding Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Save to Audit Table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save to Quarantine Table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save to Quarantine Table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save to Quarantine Table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save to Quarantine Table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save to Quarantine Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Source Truth Anchor": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Update row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update row(s)": {
      "main": [
        [
          {
            "node": "Respond to Webhook3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook1": {
      "main": [
        [
          {
            "node": "Update row(s)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update row(s)1": {
      "main": [
        [
          {
            "node": "Respond to Webhook4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Semantic Rule Engine": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Radical Grounding Check": {
      "main": [
        [
          {
            "node": "Deterministic Validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Save to dpi-block-log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to dpi-block-log": {
      "main": [
        [
          {
            "node": "Respond to Webhook1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "a71c88ac-a3a9-4a02-85f9-186c2b99f8d8",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "GdSO0enNbTIDWDwL",
  "tags": []
}