{
  "name": "TestNeo \u2014 Post-agent verification (Template 1)",
  "nodes": [
    {
      "id": "m1",
      "name": "When clicking Test workflow",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        300
      ],
      "parameters": {}
    },
    {
      "id": "w1",
      "name": "Webhook \u2014 agent done",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        80
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "testneo-agent-done",
        "responseMode": "lastNode",
        "options": {}
      },
      "notes": "POST JSON like fixtures/webhook_agent_done.json. Or use Manual Trigger + Edit Config.",
      "notesInFlow": true
    },
    {
      "id": "c1",
      "name": "Edit Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        280,
        200
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "baseUrl",
              "value": "={{ $env.TESTNEO_BASE_URL || 'https://app.testneo.ai' }}",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "webAppUrl",
              "value": "={{ $env.TESTNEO_WEB_APP_URL || 'https://app.testneo.ai' }}",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "apiKey",
              "value": "={{ $env.TESTNEO_API_KEY || $json.api_key || '' }}",
              "type": "string"
            },
            {
              "id": "a4",
              "name": "projectId",
              "value": "={{ Number($json.project_id || $env.TESTNEO_PROJECT_ID || 0) }}",
              "type": "number"
            },
            {
              "id": "a5",
              "name": "testCaseId",
              "value": "={{ Number($json.test_case_id || $env.TESTNEO_TEST_CASE_ID || 0) }}",
              "type": "number"
            },
            {
              "id": "a6",
              "name": "environmentName",
              "value": "={{ $json.environment_name || $env.TESTNEO_ENVIRONMENT_NAME || 'staging' }}",
              "type": "string"
            },
            {
              "id": "a7",
              "name": "expectedMeaning",
              "value": "={{ $json.expected_meaning || 'Backpack was added to the cart successfully' }}",
              "type": "string"
            },
            {
              "id": "a8",
              "name": "agentClaim",
              "value": "={{ $json.agent_claim || $json.agent_run_summary?.agent_claim || '' }}",
              "type": "string"
            },
            {
              "id": "a9",
              "name": "skipIngest",
              "value": "={{ Boolean($json.skip_ingest) }}",
              "type": "boolean"
            },
            {
              "id": "a10",
              "name": "skipSemantic",
              "value": "={{ Boolean($json.skip_semantic) }}",
              "type": "boolean"
            },
            {
              "id": "a11",
              "name": "agentRunSummary",
              "value": "={{ $json.agent_run_summary || {} }}",
              "type": "object"
            },
            {
              "id": "a12",
              "name": "pollMax",
              "value": 60,
              "type": "number"
            },
            {
              "id": "a13",
              "name": "pollIntervalSec",
              "value": 5,
              "type": "number"
            },
            {
              "id": "a14",
              "name": "useAgent",
              "value": "={{ ($env.TESTNEO_USE_AGENT || 'false').toLowerCase() === 'true' || Boolean($json.use_agent) }}",
              "type": "boolean"
            }
          ]
        },
        "options": {}
      },
      "notes": "Prefer env vars TESTNEO_* on self-hosted n8n. Never commit API keys.",
      "notesInFlow": true
    },
    {
      "id": "if_ingest",
      "name": "Skip ingest?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        520,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.skipIngest }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "http_ingest",
      "name": "Ingest AgentRunSummary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        760,
        80
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ ($('Edit Config').item.json.baseUrl || '').replace(/\\/$/, '') }}/api/web/v1/projects/{{ $('Edit Config').item.json.projectId }}/unified-contexts/ingest/agent-run",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Edit Config').item.json.apiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ summary: Object.keys($('Edit Config').item.json.agentRunSummary || {}).length ? $('Edit Config').item.json.agentRunSummary : { contract_version: 'agent_run_summary.v1', source: 'n8n', agent_run_id: 'n8n-' + Date.now(), goal: 'Agent task', outcome: 'success', agent_claim: $('Edit Config').item.json.agentClaim, touched: { urls: [], api: [], tools: [], ui_actions: [] }, errors: [] }, upsert: true, generate_tests: false, max_tests: 6, include_ui_tests: true, include_api_tests: true }) }}",
        "options": {
          "timeout": 120000
        }
      }
    },
    {
      "id": "if_sem",
      "name": "Skip semantic?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1200,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c2",
              "leftValue": "={{ $('Edit Config').item.json.skipSemantic }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "http_sem",
      "name": "Semantic Assert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1440,
        80
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ ($('Edit Config').item.json.baseUrl || '').replace(/\\/$/, '') }}/api/web/v1/semantic-assert",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Edit Config').item.json.apiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ actual: $('Edit Config').item.json.agentClaim, expected_meaning: $('Edit Config').item.json.expectedMeaning, threshold: 0.55, project_id: $('Edit Config').item.json.projectId }) }}",
        "options": {
          "timeout": 60000
        }
      }
    },
    {
      "id": "if_sem_ok",
      "name": "Semantic passed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1680,
        80
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c3",
              "leftValue": "={{ $json.passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "fail_sem",
      "name": "BLOCK \u2014 semantic fail",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1920,
        -40
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "f1",
              "name": "ok",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "f2",
              "name": "blocked_by",
              "value": "semantic_assert",
              "type": "string"
            },
            {
              "id": "f3",
              "name": "detail",
              "value": "={{ $json }}",
              "type": "object"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "http_exec",
      "name": "Execute golden test",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2160,
        200
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ ($('Edit Config').item.json.baseUrl || '').replace(/\\/$/, '') }}/api/web/v1/test-cases/{{ $('Edit Config').item.json.testCaseId }}/execute",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Edit Config').item.json.apiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify(Object.assign({ execution_source: 'n8n_post_agent_verification', trigger_reason: 'post_agent_gate', environment_name: $('Edit Config').item.json.environmentName }, $('Edit Config').item.json.useAgent ? { use_agent: true } : {})) }}",
        "options": {
          "timeout": 180000
        }
      }
    },
    {
      "id": "code_poll",
      "name": "Poll until terminal",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2400,
        200
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "const cfg = $('Edit Config').first().json;\nconst base = String(cfg.baseUrl || '').replace(/\\/$/, '');\nconst web = String(cfg.webAppUrl || base).replace(/\\/$/, '');\nconst key = cfg.apiKey;\nconst execResp = items[0].json;\nconst executionId = execResp.execution_id || (execResp.data && execResp.data.execution_id);\nif (!executionId) {\n  throw new Error('No execution_id in execute response: ' + JSON.stringify(execResp).slice(0, 400));\n}\nconst terminal = new Set(['passed','pass','success','successful','completed','complete','failed','fail','error','errored','timed_out','timeout','cancelled','canceled','aborted','terminated']);\nconst passedSet = new Set(['passed','pass','success','successful','completed','complete']);\nconst maxPolls = Number(cfg.pollMax || 60);\nconst intervalMs = Number(cfg.pollIntervalSec || 5) * 1000;\nlet status = 'running';\nlet summary = {};\nfor (let i = 0; i < maxPolls; i++) {\n  await new Promise(r => setTimeout(r, intervalMs));\n  summary = await this.helpers.httpRequest({\n    method: 'GET',\n    url: `${base}/api/web/v1/analytics/execution/${executionId}/summary`,\n    headers: { Authorization: `Bearer ${key}`, Accept: 'application/json' },\n    json: true,\n  });\n  status = String(summary.canonical_status || summary.status || summary.execution_status || 'running');\n  if (terminal.has(status.toLowerCase())) break;\n}\nconst ok = passedSet.has(status.toLowerCase());\nreturn [{\n  json: {\n    ok,\n    execution_id: executionId,\n    status,\n    dashboard_url: `${web}/test-runner/execution/${executionId}`,\n    project_id: cfg.projectId,\n    test_case_id: cfg.testCaseId,\n    summary,\n  }\n}];\n"
      },
      "notes": "Requires n8n Code node helpers.httpRequest (n8n 1.40+). Same poll logic as demo-agent-checkout.",
      "notesInFlow": true
    },
    {
      "id": "if_pass",
      "name": "Golden test passed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2640,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c4",
              "leftValue": "={{ $json.ok }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "pass_out",
      "name": "PASS \u2014 allow promote",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2880,
        80
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "p1",
              "name": "ok",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "p2",
              "name": "verdict",
              "value": "PASS",
              "type": "string"
            },
            {
              "id": "p3",
              "name": "execution_id",
              "value": "={{ $json.execution_id }}",
              "type": "string"
            },
            {
              "id": "p4",
              "name": "dashboard_url",
              "value": "={{ $json.dashboard_url }}",
              "type": "string"
            },
            {
              "id": "p5",
              "name": "status",
              "value": "={{ $json.status }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "fail_out",
      "name": "BLOCK \u2014 golden fail",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2880,
        320
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "b1",
              "name": "ok",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "b2",
              "name": "verdict",
              "value": "BLOCK",
              "type": "string"
            },
            {
              "id": "b3",
              "name": "execution_id",
              "value": "={{ $json.execution_id }}",
              "type": "string"
            },
            {
              "id": "b4",
              "name": "dashboard_url",
              "value": "={{ $json.dashboard_url }}",
              "type": "string"
            },
            {
              "id": "b5",
              "name": "status",
              "value": "={{ $json.status }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    }
  ],
  "connections": {
    "When clicking Test workflow": {
      "main": [
        [
          {
            "node": "Edit Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook \u2014 agent done": {
      "main": [
        [
          {
            "node": "Edit Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Config": {
      "main": [
        [
          {
            "node": "Skip ingest?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip ingest?": {
      "main": [
        [
          {
            "node": "Skip semantic?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Ingest AgentRunSummary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ingest AgentRunSummary": {
      "main": [
        [
          {
            "node": "Skip semantic?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip semantic?": {
      "main": [
        [
          {
            "node": "Execute golden test",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Semantic Assert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Semantic Assert": {
      "main": [
        [
          {
            "node": "Semantic passed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Semantic passed?": {
      "main": [
        [
          {
            "node": "Execute golden test",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "BLOCK \u2014 semantic fail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute golden test": {
      "main": [
        [
          {
            "node": "Poll until terminal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll until terminal": {
      "main": [
        [
          {
            "node": "Golden test passed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Golden test passed?": {
      "main": [
        [
          {
            "node": "PASS \u2014 allow promote",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "BLOCK \u2014 golden fail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": [
    {
      "name": "testneo"
    },
    {
      "name": "ai-agent-verification"
    }
  ],
  "pinData": {}
}
