{
  "slug": "n8n",
  "status": "passed",
  "checkedAt": "2026-10-02T05:01:55.516Z",
  "label": "Local n8n workflow execution",
  "scope": "Actual n8n 2.41.5 native CSV, Set, If and HTTP Request nodes, run through imported CLI workflows; deterministic placeholder drafts and a fault-injected localhost idempotent sink. No external account, model generation, hosted connector or public publication was tested.",
  "conclusion": "All six native workflow control cases passed. Three CSV rows produced exactly two approved local draft files and one rejection. Missing approval and an external action scope were blocked. A permanent output error stopped after three 503 attempts; environment access was denied without leaking the synthetic marker. Retrying C also repeated A three times: the local sink deduplicated those requests by ID, so this result does not claim that n8n automatically makes external connector side effects idempotent.",
  "artifact": "/evidence/n8n-workflow-control.json",
  "engine": {
    "name": "n8n",
    "version": "2.41.5",
    "nodeVersion": "v24.21.0",
    "workflowCommand": "import:workflow then execute --id --rawOutput",
    "startedAt": "2026-10-02T04:58:39.502Z",
    "completedAt": "2026-10-02T05:01:55.516Z"
  },
  "checks": [
    {
      "id": "approval-retry",
      "status": "passed",
      "cliExitCode": 0,
      "executionId": "1",
      "engineStatus": "success",
      "error": null,
      "runNodes": [
        "Manual Trigger",
        "Read sandbox CSV",
        "Parse ideas",
        "Draft placeholder",
        "Valid local approval request",
        "Approved by human",
        "Write local idempotent sink",
        "Rejected approval"
      ],
      "requests": [
        {
          "case": "approval-retry",
          "id": "idea-A",
          "attempt": 1,
          "at": "2026-10-02T05:01:04.589Z",
          "path": "/draft",
          "statusCode": 200,
          "action": "created"
        },
        {
          "case": "approval-retry",
          "id": "idea-C",
          "attempt": 1,
          "at": "2026-10-02T05:01:04.594Z",
          "path": "/draft",
          "statusCode": 503,
          "action": "injected-failure"
        },
        {
          "case": "approval-retry",
          "id": "idea-A",
          "attempt": 2,
          "at": "2026-10-02T05:01:05.280Z",
          "path": "/draft",
          "statusCode": 200,
          "action": "idempotent-existing"
        },
        {
          "case": "approval-retry",
          "id": "idea-C",
          "attempt": 2,
          "at": "2026-10-02T05:01:05.281Z",
          "path": "/draft",
          "statusCode": 503,
          "action": "injected-failure"
        },
        {
          "case": "approval-retry",
          "id": "idea-A",
          "attempt": 3,
          "at": "2026-10-02T05:01:05.601Z",
          "path": "/draft",
          "statusCode": 200,
          "action": "idempotent-existing"
        },
        {
          "case": "approval-retry",
          "id": "idea-C",
          "attempt": 3,
          "at": "2026-10-02T05:01:05.602Z",
          "path": "/draft",
          "statusCode": 200,
          "action": "created"
        }
      ],
      "acceptedFiles": [
        "idea-A.json",
        "idea-C.json"
      ],
      "assertions": [
        {
          "name": "Native CSV parser emitted all three rows",
          "pass": true
        },
        {
          "name": "Two approved files and no rejected file",
          "pass": true
        },
        {
          "name": "Rejected branch captured B",
          "pass": true
        },
        {
          "name": "C succeeded only after two injected failures",
          "pass": true
        },
        {
          "name": "n8n execution succeeded",
          "pass": true
        }
      ],
      "startedAt": "2026-10-02T05:00:38.410Z",
      "runDataStartedAt": "2026-10-02T05:01:04.383Z",
      "stoppedAt": "2026-10-02T05:01:05.617Z"
    },
    {
      "id": "duplicate-replay",
      "status": "passed",
      "cliExitCode": 0,
      "executionId": "2",
      "engineStatus": "success",
      "error": null,
      "runNodes": [
        "Manual Trigger",
        "Read sandbox CSV",
        "Parse ideas",
        "Draft placeholder",
        "Valid local approval request",
        "Approved by human",
        "Write local idempotent sink",
        "Rejected approval"
      ],
      "requests": [
        {
          "case": "duplicate-replay",
          "id": "idea-A",
          "attempt": 1,
          "at": "2026-10-02T05:01:14.740Z",
          "path": "/draft",
          "statusCode": 200,
          "action": "idempotent-existing"
        },
        {
          "case": "duplicate-replay",
          "id": "idea-C",
          "attempt": 1,
          "at": "2026-10-02T05:01:14.742Z",
          "path": "/draft",
          "statusCode": 200,
          "action": "idempotent-existing"
        }
      ],
      "acceptedFiles": [
        "idea-A.json",
        "idea-C.json"
      ],
      "assertions": [
        {
          "name": "n8n replay succeeded",
          "pass": true
        },
        {
          "name": "Replay retained exactly two files",
          "pass": true
        },
        {
          "name": "All replay writes recognized existing IDs",
          "pass": true
        }
      ],
      "startedAt": "2026-10-02T05:01:12.822Z",
      "runDataStartedAt": "2026-10-02T05:01:14.557Z",
      "stoppedAt": "2026-10-02T05:01:14.765Z"
    },
    {
      "id": "missing-approval",
      "status": "passed",
      "cliExitCode": 0,
      "executionId": "3",
      "engineStatus": "success",
      "error": null,
      "runNodes": [
        "Manual Trigger",
        "Read sandbox CSV",
        "Parse ideas",
        "Draft placeholder",
        "Valid local approval request",
        "Blocked invalid request"
      ],
      "requests": [],
      "acceptedFiles": [
        "idea-A.json",
        "idea-C.json"
      ],
      "assertions": [
        {
          "name": "Missing approval reached blocked branch",
          "pass": true
        },
        {
          "name": "No sink requests or new output",
          "pass": true
        },
        {
          "name": "n8n execution completed controlled branch",
          "pass": true
        }
      ],
      "startedAt": "2026-10-02T05:01:22.057Z",
      "runDataStartedAt": "2026-10-02T05:01:23.887Z",
      "stoppedAt": "2026-10-02T05:01:24.021Z"
    },
    {
      "id": "wrong-action-scope",
      "status": "passed",
      "cliExitCode": 0,
      "executionId": "4",
      "engineStatus": "success",
      "error": null,
      "runNodes": [
        "Manual Trigger",
        "Read sandbox CSV",
        "Parse ideas",
        "Draft placeholder",
        "Valid local approval request",
        "Blocked invalid request"
      ],
      "requests": [],
      "acceptedFiles": [
        "idea-A.json",
        "idea-C.json"
      ],
      "assertions": [
        {
          "name": "External action scope reached blocked branch",
          "pass": true
        },
        {
          "name": "No sink requests or new output",
          "pass": true
        },
        {
          "name": "n8n execution completed controlled branch",
          "pass": true
        }
      ],
      "startedAt": "2026-10-02T05:01:31.018Z",
      "runDataStartedAt": "2026-10-02T05:01:32.731Z",
      "stoppedAt": "2026-10-02T05:01:32.851Z"
    },
    {
      "id": "persistent-output-failure",
      "status": "passed",
      "cliExitCode": 1,
      "executionId": "5",
      "engineStatus": "error",
      "error": "Service unavailable - try again later or consider setting this node to retry automatically (in the node settings)",
      "runNodes": [
        "Manual Trigger",
        "Read sandbox CSV",
        "Parse ideas",
        "Draft placeholder",
        "Valid local approval request",
        "Approved by human",
        "Write local idempotent sink"
      ],
      "requests": [
        {
          "case": "persistent-output-failure",
          "id": "idea-FAIL",
          "attempt": 1,
          "at": "2026-10-02T05:01:41.535Z",
          "path": "/draft",
          "statusCode": 503,
          "action": "injected-failure"
        },
        {
          "case": "persistent-output-failure",
          "id": "idea-FAIL",
          "attempt": 2,
          "at": "2026-10-02T05:01:41.921Z",
          "path": "/draft",
          "statusCode": 503,
          "action": "injected-failure"
        },
        {
          "case": "persistent-output-failure",
          "id": "idea-FAIL",
          "attempt": 3,
          "at": "2026-10-02T05:01:42.232Z",
          "path": "/draft",
          "statusCode": 503,
          "action": "injected-failure"
        }
      ],
      "acceptedFiles": [
        "idea-A.json",
        "idea-C.json"
      ],
      "assertions": [
        {
          "name": "n8n recorded output node error",
          "pass": true
        },
        {
          "name": "Native retry stopped after three 503 attempts",
          "pass": true
        },
        {
          "name": "Failed ID has no accepted file",
          "pass": true
        }
      ],
      "startedAt": "2026-10-02T05:01:39.658Z",
      "runDataStartedAt": "2026-10-02T05:01:41.387Z",
      "stoppedAt": "2026-10-02T05:01:42.237Z"
    },
    {
      "id": "environment-secret-boundary",
      "status": "passed",
      "cliExitCode": 1,
      "executionId": "6",
      "error": "access to env vars denied",
      "assertions": [
        {
          "name": "Native $env access denied",
          "pass": true
        },
        {
          "name": "Synthetic marker absent from execution logs and outputs",
          "pass": true
        }
      ],
      "engineStatus": "error",
      "startedAt": "2026-10-02T05:01:55.145Z",
      "runDataStartedAt": "2026-10-02T05:01:55.195Z",
      "stoppedAt": "2026-10-02T05:01:55.330Z"
    }
  ],
  "network": {
    "runtimeBoundary": "Node preload permits loopback/Windows local pipe only; external HTTP, socket and DNS attempts are blocked and logged. Installation used npm and official Node distribution downloads.",
    "attemptCount": 28,
    "blockedExternalAttempts": []
  },
  "limitations": [
    "Drafts are fixed placeholders, not generated by an LLM.",
    "Approval is a sandbox CSV field, not a tested remote human approval integration.",
    "The local sink performs ID-based deduplication; native node retries can repeat earlier successful requests.",
    "CLI exit code is not the success oracle: n8n resultData.error, runData, requests and persisted files were checked."
  ],
  "fixture": {
    "workflow": {
      "id": "UagentkitN8nControl",
      "name": "uAgentKit isolated approval / retry fixture",
      "active": false,
      "nodes": [
        {
          "id": "afcc1b36-4ff8-48d0-a2d0-eb7ac4b7d627",
          "name": "Manual Trigger",
          "type": "n8n-nodes-base.manualTrigger",
          "typeVersion": 1,
          "position": [
            0,
            0
          ],
          "parameters": {}
        },
        {
          "id": "1af1b7fd-ad1c-444c-bb35-2a8c259ceb3e",
          "name": "Read sandbox CSV",
          "type": "n8n-nodes-base.readWriteFile",
          "typeVersion": 1.1,
          "position": [
            200,
            0
          ],
          "parameters": {
            "operation": "read",
            "fileSelector": "fixtures/ideas.csv",
            "options": {
              "dataPropertyName": "data"
            }
          }
        },
        {
          "id": "c1efa3a3-2303-4df9-af00-312674e7f079",
          "name": "Parse ideas",
          "type": "n8n-nodes-base.extractFromFile",
          "typeVersion": 1.1,
          "position": [
            400,
            0
          ],
          "parameters": {
            "operation": "csv",
            "binaryPropertyName": "data",
            "options": {
              "headerRow": true,
              "includeEmptyCells": true
            }
          }
        },
        {
          "id": "881ba876-9cca-4de2-8d58-2fc31c18d2c7",
          "name": "Draft placeholder",
          "type": "n8n-nodes-base.set",
          "typeVersion": 3.4,
          "position": [
            600,
            0
          ],
          "parameters": {
            "mode": "manual",
            "includeOtherFields": true,
            "assignments": {
              "assignments": [
                {
                  "id": "95c27650-dde5-404c-8294-52905f295781",
                  "name": "draft",
                  "value": "={{ \"Placeholder draft: \" + $json.title }}",
                  "type": "string"
                },
                {
                  "id": "9aa192b6-4ae0-4f05-9623-cd4bc482587d",
                  "name": "modelGenerated",
                  "value": false,
                  "type": "boolean"
                }
              ]
            },
            "options": {}
          }
        },
        {
          "id": "291573d7-cb58-40c5-9156-b966238b1089",
          "name": "Valid local approval request",
          "type": "n8n-nodes-base.if",
          "typeVersion": 2.2,
          "position": [
            800,
            0
          ],
          "parameters": {
            "conditions": {
              "combinator": "and",
              "options": {
                "caseSensitive": true,
                "leftValue": "",
                "typeValidation": "strict",
                "version": 2
              },
              "conditions": [
                {
                  "id": "7f057a2d-214d-4a1b-9f06-b612d8d19f20",
                  "leftValue": "={{ $json.id || \"\" }}",
                  "rightValue": "",
                  "operator": {
                    "type": "string",
                    "operation": "notEmpty"
                  }
                },
                {
                  "id": "09086b08-1fea-4848-94ef-18ce991b9457",
                  "leftValue": "={{ $json.title || \"\" }}",
                  "rightValue": "",
                  "operator": {
                    "type": "string",
                    "operation": "notEmpty"
                  }
                },
                {
                  "id": "0363c76e-a941-4f84-a23f-baf78d73be46",
                  "leftValue": "={{ $json.approval || \"\" }}",
                  "rightValue": "",
                  "operator": {
                    "type": "string",
                    "operation": "notEmpty"
                  }
                },
                {
                  "id": "a161a61b-567a-49e5-ba20-e0d70c284faf",
                  "leftValue": "={{ $json.scope || \"\" }}",
                  "rightValue": "local-draft",
                  "operator": {
                    "type": "string",
                    "operation": "equals"
                  }
                }
              ]
            },
            "options": {
              "ignoreCase": false
            }
          }
        },
        {
          "id": "91cf654b-3136-4fae-99aa-e12c9e31c17b",
          "name": "Approved by human",
          "type": "n8n-nodes-base.if",
          "typeVersion": 2.2,
          "position": [
            1000,
            0
          ],
          "parameters": {
            "conditions": {
              "combinator": "and",
              "options": {
                "caseSensitive": true,
                "leftValue": "",
                "typeValidation": "strict",
                "version": 2
              },
              "conditions": [
                {
                  "id": "b920dc7a-8a4e-477b-bf46-05c6c0cfdd9f",
                  "leftValue": "={{ $json.approval }}",
                  "rightValue": "approved",
                  "operator": {
                    "type": "string",
                    "operation": "equals"
                  }
                }
              ]
            },
            "options": {
              "ignoreCase": false
            }
          }
        },
        {
          "id": "e0a19059-61db-4481-a178-065e290ae6e4",
          "name": "Blocked invalid request",
          "type": "n8n-nodes-base.set",
          "typeVersion": 3.4,
          "position": [
            1000,
            250
          ],
          "parameters": {
            "mode": "manual",
            "includeOtherFields": true,
            "assignments": {
              "assignments": [
                {
                  "id": "b8729ad1-0873-4488-8800-077f556f15f5",
                  "name": "result",
                  "value": "blocked-invalid-input-or-scope",
                  "type": "string"
                }
              ]
            },
            "options": {}
          }
        },
        {
          "id": "88d27c74-1c6a-4d88-847c-4f29ec8e8bcd",
          "name": "Rejected approval",
          "type": "n8n-nodes-base.set",
          "typeVersion": 3.4,
          "position": [
            1200,
            200
          ],
          "parameters": {
            "mode": "manual",
            "includeOtherFields": true,
            "assignments": {
              "assignments": [
                {
                  "id": "e9e3e023-60af-4549-a5e8-d2585bca70f6",
                  "name": "result",
                  "value": "rejected",
                  "type": "string"
                }
              ]
            },
            "options": {}
          }
        },
        {
          "id": "4c2f8d3d-9e2d-44f2-87d6-8ef2362290c4",
          "name": "Write local idempotent sink",
          "type": "n8n-nodes-base.httpRequest",
          "typeVersion": 4.2,
          "position": [
            1200,
            0
          ],
          "retryOnFail": true,
          "maxTries": 3,
          "waitBetweenTries": 300,
          "parameters": {
            "method": "POST",
            "url": "http://127.0.0.1:1166/draft",
            "sendBody": true,
            "contentType": "json",
            "specifyBody": "json",
            "jsonBody": "={{ { id: $json.id, title: $json.title, approval: $json.approval, scope: $json.scope, draft: $json.draft, modelGenerated: $json.modelGenerated } }}",
            "options": {
              "timeout": 5000
            }
          }
        }
      ],
      "connections": {
        "Manual Trigger": {
          "main": [
            [
              {
                "node": "Read sandbox CSV",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Read sandbox CSV": {
          "main": [
            [
              {
                "node": "Parse ideas",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Parse ideas": {
          "main": [
            [
              {
                "node": "Draft placeholder",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Draft placeholder": {
          "main": [
            [
              {
                "node": "Valid local approval request",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Valid local approval request": {
          "main": [
            [
              {
                "node": "Approved by human",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Blocked invalid request",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Approved by human": {
          "main": [
            [
              {
                "node": "Write local idempotent sink",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Rejected approval",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      },
      "settings": {
        "executionOrder": "v1",
        "saveDataSuccessExecution": "all",
        "saveDataErrorExecution": "all"
      }
    },
    "inputFixtures": [
      {
        "case": "approval-retry",
        "csv": "id,title,approval,scope\nidea-A,Alpha article,approved,local-draft\nidea-B,Beta rejected,rejected,local-draft\nidea-C,Gamma retry,approved,local-draft\n"
      },
      {
        "case": "duplicate-replay",
        "csv": "id,title,approval,scope\nidea-A,Alpha article,approved,local-draft\nidea-B,Beta rejected,rejected,local-draft\nidea-C,Gamma retry,approved,local-draft\n"
      },
      {
        "case": "missing-approval",
        "csv": "id,title,approval,scope\nidea-MISSING,Missing approval,,local-draft\n"
      },
      {
        "case": "wrong-action-scope",
        "csv": "id,title,approval,scope\nidea-SCOPE,External scope,approved,external-publish\n"
      },
      {
        "case": "persistent-output-failure",
        "csv": "id,title,approval,scope\nidea-FAIL,Always fail,approved,local-draft\n"
      }
    ],
    "outputs": [
      {
        "file": "idea-A.json",
        "data": {
          "id": "idea-A",
          "title": "Alpha article",
          "approval": "approved",
          "scope": "local-draft",
          "draft": "Placeholder draft: Alpha article",
          "modelGenerated": false
        }
      },
      {
        "file": "idea-C.json",
        "data": {
          "id": "idea-C",
          "title": "Gamma retry",
          "approval": "approved",
          "scope": "local-draft",
          "draft": "Placeholder draft: Gamma retry",
          "modelGenerated": false
        }
      }
    ],
    "placeholderNote": "Draft text is deliberately deterministic. This fixture measures workflow control and side effects, not content quality or LLM output.",
    "idempotencyNote": "The local sink enforces one file per ID. n8n retries may repeat successful HTTP requests; this is not a built-in connector idempotency guarantee."
  },
  "reproducibility": {
    "package": "n8n@2.41.5",
    "packageLockSha256": "bd846f3a192835bce0ba871690ef29cbfe0f9393df447e39b7be7f943ba7c475",
    "source": "https://www.npmjs.com/package/n8n/v/2.41.5",
    "commands": [
      "Import the inactive workflow with n8n import:workflow --input=workflow.json",
      "Execute n8n execute --id=UagentkitN8nControl --rawOutput against an isolated instance and loopback sink"
    ],
    "networkNote": "The workflow CLI process uses a Node preload that blocks external HTTP, socket and DNS connections. No internal task runner is spawned; Code nodes are excluded. Other workflow environments must apply their own network controls."
  }
}
