{
  "name": "Hand off a task to someone else",
  "nodes": [
    {
      "parameters": {
        "content": "## Hand off a task to someone else\nTurn a task you do in your head into a plain hand-off for another person, with the materials listed, the steps written, nothing sensitive left in, and a fresh reader's questions answered.\n\n**How to use**\n1. Open **Chat model** and pick your own credential. To use another AI service, delete that node, add the service's chat model node, and connect it to the Model plug under every step and AI check.\n2. Open **Inputs** and paste your material into each field. Examples are filled in so you can try it first.\n3. Click **Execute workflow**.\n4. Open **Result** to read the answer, anything flagged, and the checks left for you.\n\n**Checks**\n- Check 1 (a plain rule, run automatically with no AI): Every row of the table has one of \"hand over\", \"keep\" or \"decide first\" in the Who column. If it fails: try once more, then flag.\n- Check 2 (a plain rule, run automatically with no AI): The answer includes the words \"Sensitive items removed:\". If it fails: try once more, then flag.\n- Check 3 (a second AI prompt answers pass or fail): Look through the final hand-over. Does it contain any password, login, account or card number, personal phone number, home address, or private detail about a person? Answer FAIL and quote it if so. If it fails: stop the run.\n- Check 4 (you check it by hand): Read the final hand-over yourself, line by line, as if it might be forwarded. Is there anything in it you would not want a stranger to see? If it fails: stop the run.\n- Check 5 (you check it by hand): Give the final hand-over to someone who does not know the task, or read it as if you did not. Could they do the first three steps without asking you anything? If it fails: flag it and carry on.\n\nFrom Logic Lab (logiclabhq.com). Free to use and adapt, with credit.",
        "height": 600,
        "width": 760,
        "color": 5
      },
      "id": "0557a9bf-5ce4-4a68-a609-7b91e6bde0f2",
      "name": "About this workflow",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -560
      ]
    },
    {
      "parameters": {},
      "id": "c77dbdbf-3581-4254-a35e-d7b961012106",
      "name": "Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        320
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "dff1e589-6aac-4b08-a557-8d5fadd0f5e6",
              "name": "task",
              "value": "Sample: Every Monday I send the weekly supplies order. I look at the stock sheet, find anything marked low, and email the order to the supplier. The supplier wants it before 11am or delivery slips a day. I use the account number on the old order email. If paper is low I order two boxes, not one, because the price is better. Last month the supplier changed the contact, so the new person is Jamie.",
              "type": "string"
            },
            {
              "id": "72d780e9-026f-493c-ae93-3b3fd6430792",
              "name": "person",
              "value": "Sample: A new part-time assistant who knows the office but has never placed an order",
              "type": "string"
            },
            {
              "id": "3d6a96d2-38ef-4715-a3af-e63ca8c2f51f",
              "name": "materials",
              "value": "Sample: Stock sheet is the file \"Supplies tracker\" on the shared drive. The supplier's email is orders@example.com.",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "9f31caf8-6019-495d-a7cc-d94eab7ac12b",
      "name": "Inputs",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        320
      ],
      "notes": "Paste your own material here: task = The task and how you do it; person = Who is taking it over; materials = Files, links or text you can include (optional).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are an experienced office manager who is good at handing work over.\n\nYour task is to break the task below into its pieces, then sort each piece by who should do it.\n\nThe task:\n{{ $(\"Inputs\").first().json[\"task\"] }}\n\nWho is taking it over: {{ $(\"Inputs\").first().json[\"person\"] }}\n\nRules:\n- Write each piece as a short action.\n- Mark a piece \"hand over\" if the new person can do it with plain steps, \"keep\" if it needs your own judgment or permission, and \"decide first\" if you must make a choice before handing it over.\n- Add pieces that you do without noticing, such as checking something first or telling someone afterward.\n- Do not write the steps yet. Do not invent pieces that the task does not suggest.\n\nReturn one markdown table and nothing else:\n| # | Piece | Who | Why |\nThe Who column must say exactly hand over, keep or decide first.{{ $(\"Check 1: Each piece is hand over, keep or decide first\").isExecuted ? \"\\n\\nYour last answer did not pass this check: Each piece is hand over, keep or decide first. What was wrong: \" + $(\"Check 1: Each piece is hand over, keep or decide first\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "5aa6b2b4-c1b2-4cf5-a779-d7f2e5e650bb",
      "name": "1. Sort the pieces",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        480,
        320
      ],
      "notes": "Split the task into pieces and sort each into hand over, keep or decide first. Returns: A table that sorts every piece into hand over, keep or decide first.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// Each piece is hand over, keep or decide first\n// Rule: Every row of the table has one of \"hand over\", \"keep\" or \"decide first\" in the Who column.\n// Runs with no AI. Returns pass (true or false) and a reason.\nconst runRule = function runRule(rule, value, work, sources) {\n    const norm = (s) => String(s ?? '')\n        .toLowerCase()\n        .replace(/[\\u2018\\u2019\\u201c\\u201d\"'`]/g, '')\n        .replace(/\\s+/g, ' ')\n        .trim();\n    const readTable = (text) => {\n        const lines = String(text ?? '')\n            .split(/\\r?\\n/)\n            .map((l) => l.trim());\n        const tables = [];\n        let cur = null;\n        const cells = (l) => l\n            .replace(/^\\|/, '')\n            .replace(/\\|$/, '')\n            .split('|')\n            .map((c) => c.trim());\n        for (let i = 0; i < lines.length; i++) {\n            const l = lines[i] ?? '';\n            if (!l.startsWith('|')) {\n                cur = null;\n                continue;\n            }\n            if (!cur) {\n                const next = lines[i + 1] ?? '';\n                if (/^\\|?\\s*:?-{2,}/.test(next)) {\n                    cur = { header: cells(l), rows: [] };\n                    tables.push(cur);\n                    i++;\n                }\n                continue;\n            }\n            cur.rows.push(cells(l));\n        }\n        return tables;\n    };\n    // Every table that has this column, with \"None found\" rows left out.\n    // Returns that column's cells from every such table, or null if no table has it.\n    const findColumn = (name) => {\n        let seen = false;\n        const cells = [];\n        for (const t of readTable(work)) {\n            const i = t.header.findIndex((h) => norm(h) === norm(name));\n            if (i < 0)\n                continue;\n            seen = true;\n            for (const r of t.rows) {\n                if (r.some((c) => norm(c) === 'none found'))\n                    continue;\n                cells.push(r[i] ?? '');\n            }\n        }\n        return seen ? cells : null;\n    };\n    const empty = (c) => !c || /^[-\\u2013\\u2014\\s]*$/.test(c);\n    switch (rule) {\n        case 'has_text':\n            return norm(work).includes(norm(value))\n                ? { pass: true, reason: 'Found.' }\n                : { pass: false, reason: `The answer does not include \"${value}\".` };\n        case 'lacks_text':\n            return norm(work).includes(norm(value))\n                ? { pass: false, reason: `The answer includes \"${value}\", which it should not.` }\n                : { pass: true, reason: 'Not found, as it should be.' };\n        case 'max_words': {\n            const n = String(work ?? '')\n                .split(/\\s+/)\n                .filter(Boolean).length;\n            return n <= Number(value)\n                ? { pass: true, reason: `${n} words.` }\n                : { pass: false, reason: `The answer has ${n} words; the limit is ${value}.` };\n        }\n        case 'table_cells_filled': {\n            const problems = [];\n            for (const col of value) {\n                const f = findColumn(col);\n                if (!f) {\n                    problems.push(`No table with a \"${col}\" column was found.`);\n                    continue;\n                }\n                f.forEach((c, i) => {\n                    if (empty(c))\n                        problems.push(`Row ${i + 1} has nothing in \"${col}\".`);\n                });\n            }\n            return problems.length\n                ? { pass: false, reason: problems.join(' ') }\n                : { pass: true, reason: 'Every row is filled in.' };\n        }\n        case 'column_values_in': {\n            const f = findColumn(value.column);\n            if (!f)\n                return { pass: false, reason: `No table with a \"${value.column}\" column was found.` };\n            const allowed = value.allowed.map(norm);\n            const bad = [];\n            f.forEach((c, i) => {\n                if (!allowed.includes(norm(c)))\n                    bad.push(`Row ${i + 1} says \"${c}\".`);\n            });\n            return bad.length\n                ? {\n                    pass: false,\n                    reason: `The \"${value.column}\" column must say ${value.allowed.join(' or ')}. ${bad.join(' ')}`,\n                }\n                : { pass: true, reason: 'Every row uses an allowed word.' };\n        }\n        case 'column_quotes_source': {\n            const f = findColumn(value.column);\n            if (!f)\n                return { pass: false, reason: `No table with a \"${value.column}\" column was found.` };\n            const src = norm(sources[value.input] ?? '');\n            const bad = [];\n            f.forEach((c, i) => {\n                // A quote may be shortened with \"...\"; each piece must be in the source.\n                const pieces = c\n                    .split(/\\.\\.\\.|\\u2026/)\n                    .map(norm)\n                    .filter(Boolean);\n                if (!pieces.length || !pieces.every((p) => src.includes(p)))\n                    bad.push(`Row ${i + 1}: \"${c}\" is not in the source.`);\n            });\n            return bad.length\n                ? { pass: false, reason: bad.join(' ') }\n                : { pass: true, reason: 'Every quote was found in the source.' };\n        }\n        case 'numbers_in_source': {\n            const nums = (s) => (String(s ?? '').match(/\\d[\\d,]*(?:\\.\\d+)?/g) ?? []).map((n) => n.replace(/,/g, ''));\n            const src = new Set(nums(sources[value.input] ?? ''));\n            const missing = [...new Set(nums(work))].filter((n) => !src.has(n));\n            return missing.length\n                ? {\n                    pass: false,\n                    reason: `These numbers are not in the source: ${missing.join(', ')}.`,\n                }\n                : { pass: true, reason: 'Every number was found in the source.' };\n        }\n        default:\n            return { pass: false, reason: `Unknown rule \"${rule}\".` };\n    }\n};\n\nconst work = String($(\"1. Sort the pieces\").first().json.text ?? '');\nconst sources = {\n\n};\nconst result = runRule(\"column_values_in\", {\"column\":\"Who\",\"allowed\":[\"hand over\",\"keep\",\"decide first\"]}, work, sources);\nreturn [{ json: result }];"
      },
      "id": "c8a5c91e-55c9-448b-a42a-4ed4b6322719",
      "name": "Check 1: Each piece is hand over, keep or decide first",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        740,
        320
      ],
      "notes": "Every row of the table has one of \"hand over\", \"keep\" or \"decide first\" in the Who column.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "2543a192-c092-4d0f-abfe-7bb0ba99e64d",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "d6295e37-4f9b-4f64-a785-f4516aaa679e",
      "name": "Passed check 1?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1000,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "81a0312d-7ae0-40fc-a910-8dbb8c1c350a",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "77e9db8d-20db-489c-a851-8adff39921d6",
      "name": "Try again? (check 1)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1000,
        520
      ],
      "notes": "True = first failure: send back to \"1. Sort the pieces\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a71193c8-601c-4a9d-acc4-3d3a0520ed4f",
              "name": "flag",
              "value": "=Each piece is hand over, keep or decide first: did not pass. {{ $(\"Check 1: Each piece is hand over, keep or decide first\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "57b45bac-94fc-4f01-a765-844628bf31a3",
      "name": "Flag check 1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1130,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are an office manager who prepares everything a new person will need.\n\nYour task is to list the materials the new person needs for the pieces marked \"hand over\", and to write any short template or example that is missing.\n\nThe sorted pieces:\n{{ $(\"1. Sort the pieces\").first().json.text }}\n\nThe task as described:\n{{ $(\"Inputs\").first().json[\"task\"] }}\n\nMaterials the person already has:\n{{ $(\"Inputs\").first().json[\"materials\"] }}\n\nRules:\n- Cover files, links, accounts to request access to, people to contact, and examples of a good result.\n- Write a short template (for example an email) only where the task shows one is needed, using only details from the task.\n- Where a detail is missing, write \"ASK: (what is missing)\". Do not invent a name, address or number.\n- Do not include passwords, account numbers or any personal details. Say where to ask for access instead.\n\nReturn two parts:\nMaterials to have ready: (a bulleted list)\nTemplates and examples: (the short templates, or \"None needed\")"
      },
      "id": "5da07509-bfe9-433c-a03d-e0ebb7da8736",
      "name": "2. List what to have ready",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        1260,
        320
      ],
      "notes": "List everything the new person needs in hand, and write any short template that is missing. Returns: A list of materials to have ready and any short templates or examples.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a trainer who writes instructions that a new person can follow alone.\n\nYour task is to write the hand-over instructions for the pieces marked \"hand over\".\n\nThe sorted pieces:\n{{ $(\"1. Sort the pieces\").first().json.text }}\n\nThe materials and templates:\n{{ $(\"2. List what to have ready\").first().json.text }}\n\nThe task as described:\n{{ $(\"Inputs\").first().json[\"task\"] }}\n\nWho is taking it over: {{ $(\"Inputs\").first().json[\"person\"] }}\n\nRules:\n- Number the steps in the order the person does them. One action per step.\n- For each step say what to do, where to find what they need, and how they will know it worked.\n- Include the reasons and the traps from the task, such as a deadline or a price tip, as a short \"Watch for\" line.\n- End with a short list called \"Stop and ask me if\" for the pieces marked \"keep\" or \"decide first\".\n- Use only what is in the task. Do not add steps that were not described.\n\nReturn the instructions in this shape:\n## What this task is\n## Before you start\n## Steps\n## Watch for\n## Stop and ask me if"
      },
      "id": "829202a7-e43e-4d2e-a1a1-a3a98515aca8",
      "name": "3. Write the plain steps",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        1520,
        320
      ],
      "notes": "Write the hand-over steps in plain words that someone new can follow without asking you. Returns: A plain hand-over note with numbered steps, things to watch for and when to ask.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a careful privacy reviewer who checks documents before they are shared.\n\nYour task is to find anything in the hand-over below that should not be handed to this person in writing, and replace it.\n\nThe hand-over:\n{{ $(\"3. Write the plain steps\").first().json.text }}\n\nWho is taking it over: {{ $(\"Inputs\").first().json[\"person\"] }}\n\nLook for passwords, logins, account or card numbers, personal phone numbers and home addresses, private details about colleagues or customers, and prices or terms that are not meant to be shared.\n\nRules:\n- Replace each item with a short note on where to get it, such as \"Ask me for the login in person\".\n- Keep everything else exactly as it is. Do not rewrite the steps.\n- If you are unsure whether something is sensitive, treat it as sensitive and list it.\n\nReturn the full hand-over with the replacements made, then:\nSensitive items removed: (a list of what kind of item and where it was, without repeating the item itself, or \"None\"){{ $(\"Check 2: The list of removed items is there\").isExecuted ? \"\\n\\nYour last answer did not pass this check: The list of removed items is there. What was wrong: \" + $(\"Check 2: The list of removed items is there\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "9a39ffd2-d023-4607-a1e7-2278905c3335",
      "name": "4. Sweep for sensitive information",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        1780,
        320
      ],
      "notes": "Find anything private or risky in the hand-over and replace it with where to ask. Returns: The hand-over with sensitive items replaced, and a list of what was removed.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// The list of removed items is there\n// Rule: The answer includes the words \"Sensitive items removed:\".\n// Runs with no AI. Returns pass (true or false) and a reason.\nconst runRule = function runRule(rule, value, work, sources) {\n    const norm = (s) => String(s ?? '')\n        .toLowerCase()\n        .replace(/[\\u2018\\u2019\\u201c\\u201d\"'`]/g, '')\n        .replace(/\\s+/g, ' ')\n        .trim();\n    const readTable = (text) => {\n        const lines = String(text ?? '')\n            .split(/\\r?\\n/)\n            .map((l) => l.trim());\n        const tables = [];\n        let cur = null;\n        const cells = (l) => l\n            .replace(/^\\|/, '')\n            .replace(/\\|$/, '')\n            .split('|')\n            .map((c) => c.trim());\n        for (let i = 0; i < lines.length; i++) {\n            const l = lines[i] ?? '';\n            if (!l.startsWith('|')) {\n                cur = null;\n                continue;\n            }\n            if (!cur) {\n                const next = lines[i + 1] ?? '';\n                if (/^\\|?\\s*:?-{2,}/.test(next)) {\n                    cur = { header: cells(l), rows: [] };\n                    tables.push(cur);\n                    i++;\n                }\n                continue;\n            }\n            cur.rows.push(cells(l));\n        }\n        return tables;\n    };\n    // Every table that has this column, with \"None found\" rows left out.\n    // Returns that column's cells from every such table, or null if no table has it.\n    const findColumn = (name) => {\n        let seen = false;\n        const cells = [];\n        for (const t of readTable(work)) {\n            const i = t.header.findIndex((h) => norm(h) === norm(name));\n            if (i < 0)\n                continue;\n            seen = true;\n            for (const r of t.rows) {\n                if (r.some((c) => norm(c) === 'none found'))\n                    continue;\n                cells.push(r[i] ?? '');\n            }\n        }\n        return seen ? cells : null;\n    };\n    const empty = (c) => !c || /^[-\\u2013\\u2014\\s]*$/.test(c);\n    switch (rule) {\n        case 'has_text':\n            return norm(work).includes(norm(value))\n                ? { pass: true, reason: 'Found.' }\n                : { pass: false, reason: `The answer does not include \"${value}\".` };\n        case 'lacks_text':\n            return norm(work).includes(norm(value))\n                ? { pass: false, reason: `The answer includes \"${value}\", which it should not.` }\n                : { pass: true, reason: 'Not found, as it should be.' };\n        case 'max_words': {\n            const n = String(work ?? '')\n                .split(/\\s+/)\n                .filter(Boolean).length;\n            return n <= Number(value)\n                ? { pass: true, reason: `${n} words.` }\n                : { pass: false, reason: `The answer has ${n} words; the limit is ${value}.` };\n        }\n        case 'table_cells_filled': {\n            const problems = [];\n            for (const col of value) {\n                const f = findColumn(col);\n                if (!f) {\n                    problems.push(`No table with a \"${col}\" column was found.`);\n                    continue;\n                }\n                f.forEach((c, i) => {\n                    if (empty(c))\n                        problems.push(`Row ${i + 1} has nothing in \"${col}\".`);\n                });\n            }\n            return problems.length\n                ? { pass: false, reason: problems.join(' ') }\n                : { pass: true, reason: 'Every row is filled in.' };\n        }\n        case 'column_values_in': {\n            const f = findColumn(value.column);\n            if (!f)\n                return { pass: false, reason: `No table with a \"${value.column}\" column was found.` };\n            const allowed = value.allowed.map(norm);\n            const bad = [];\n            f.forEach((c, i) => {\n                if (!allowed.includes(norm(c)))\n                    bad.push(`Row ${i + 1} says \"${c}\".`);\n            });\n            return bad.length\n                ? {\n                    pass: false,\n                    reason: `The \"${value.column}\" column must say ${value.allowed.join(' or ')}. ${bad.join(' ')}`,\n                }\n                : { pass: true, reason: 'Every row uses an allowed word.' };\n        }\n        case 'column_quotes_source': {\n            const f = findColumn(value.column);\n            if (!f)\n                return { pass: false, reason: `No table with a \"${value.column}\" column was found.` };\n            const src = norm(sources[value.input] ?? '');\n            const bad = [];\n            f.forEach((c, i) => {\n                // A quote may be shortened with \"...\"; each piece must be in the source.\n                const pieces = c\n                    .split(/\\.\\.\\.|\\u2026/)\n                    .map(norm)\n                    .filter(Boolean);\n                if (!pieces.length || !pieces.every((p) => src.includes(p)))\n                    bad.push(`Row ${i + 1}: \"${c}\" is not in the source.`);\n            });\n            return bad.length\n                ? { pass: false, reason: bad.join(' ') }\n                : { pass: true, reason: 'Every quote was found in the source.' };\n        }\n        case 'numbers_in_source': {\n            const nums = (s) => (String(s ?? '').match(/\\d[\\d,]*(?:\\.\\d+)?/g) ?? []).map((n) => n.replace(/,/g, ''));\n            const src = new Set(nums(sources[value.input] ?? ''));\n            const missing = [...new Set(nums(work))].filter((n) => !src.has(n));\n            return missing.length\n                ? {\n                    pass: false,\n                    reason: `These numbers are not in the source: ${missing.join(', ')}.`,\n                }\n                : { pass: true, reason: 'Every number was found in the source.' };\n        }\n        default:\n            return { pass: false, reason: `Unknown rule \"${rule}\".` };\n    }\n};\n\nconst work = String($(\"4. Sweep for sensitive information\").first().json.text ?? '');\nconst sources = {\n\n};\nconst result = runRule(\"has_text\", \"Sensitive items removed:\", work, sources);\nreturn [{ json: result }];"
      },
      "id": "6288420b-28f5-4f64-a600-0235f220d506",
      "name": "Check 2: The list of removed items is there",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2040,
        320
      ],
      "notes": "The answer includes the words \"Sensitive items removed:\".",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "2643a325-bf92-4b7c-acfe-7d43b999e4ba",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "d6271fa0-cf9e-477b-a78d-7996eaa2e259",
      "name": "Passed check 2?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2300,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "80a02f9a-7be0-428f-a810-8c288d1c369d",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "4fe75dfe-80d3-40f7-a859-10245b9c0425",
      "name": "Try again? (check 2)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2300,
        520
      ],
      "notes": "True = first failure: send back to \"4. Sweep for sensitive information\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a811955b-5f1c-490a-adc4-3ecd0420ebbc",
              "name": "flag",
              "value": "=The list of removed items is there: did not pass. {{ $(\"Check 2: The list of removed items is there\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "5ab46065-91fc-4a48-a665-82b329bf3336",
      "name": "Flag check 2",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2430,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a person who has never done this task and knows nothing about it except what is written below.\n\nYour task is to read the hand-over below as if you had to do the work tomorrow, and list every point where you would get stuck or have to ask a question.\n\nThe hand-over:\n{{ $(\"4. Sweep for sensitive information\").first().json.text }}\n\nRules:\n- Only use what is written in the hand-over. Do not guess what the writer meant.\n- List a question for each gap: a step with no detail, a word you would not know, a place you could not find, a missing person's name.\n- Point to the exact words where you got stuck.\n- Do not rewrite the hand-over. If you find no gaps, say so, but look hard first.\n\nReturn one markdown table and nothing else:\n| # | Where (quote) | Question I would have to ask |\nIf you have no questions, keep the table and add one row that says \"None found\"."
      },
      "id": "df3a9923-7ef4-4ee0-adf9-8fb9b4265b5e",
      "name": "5. Read it as someone new",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        2560,
        320
      ],
      "notes": "Read only the hand-over, as the new person would, and list everything missing or unclear. Returns: A table of questions a fresh reader would have, each pointing to the exact words.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are the person who wrote the hand-over, now fixing it after a fresh reader's questions.\n\nYour task is to produce the final hand-over, with every question answered where the task description allows.\n\nThe hand-over:\n{{ $(\"4. Sweep for sensitive information\").first().json.text }}\n\nThe fresh reader's questions:\n{{ $(\"5. Read it as someone new\").first().json.text }}\n\nThe task as described:\n{{ $(\"Inputs\").first().json[\"task\"] }}\n\nRules:\n- Answer a question only with details from the task description. Do not invent an answer.\n- If the answer is not in the task description, add a line \"ASK: (the question)\" in the right place, and list all of them at the end under \"Still to fill in\".\n- Do not add any password, account number or personal detail.\n- Do not change the parts of the hand-over that no question touched.\n\nReturn the full final hand-over in the same shape as before, then:\n## Still to fill in\n(the ASK lines, or \"None\")"
      },
      "id": "b88beedb-029b-44e0-a5e8-58793738c1a6",
      "name": "6. Close the gaps",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        2820,
        320
      ],
      "notes": "Answer each question from what you wrote about the task, and mark the ones only you can answer. Returns: The final hand-over, ready to give to the person, with a short list of what you still need to fill in.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a careful reviewer. You do not fix or rewrite the work; you only decide whether it passes one check.\n\nThe check: Nothing sensitive is left in\nLook through the final hand-over. Does it contain any password, login, account or card number, personal phone number, home address, or private detail about a person? Answer FAIL and quote it if so.\n\nThe work to check (the answer from Step 6 (Close the gaps)):\n{{ $(\"6. Close the gaps\").first().json.text }}\n\nReply with exactly PASS or FAIL on the first line. On the second line, say in one sentence why. If it fails, name the exact item or line that fails. Do not add anything else."
      },
      "id": "c819f62b-01f1-4c54-a6ef-5dadbe673ed6",
      "name": "Check 3: Nothing sensitive is left in",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        3080,
        320
      ],
      "notes": "Answers PASS or FAIL. Look through the final hand-over. Does it contain any password, login, account or card number, personal phone number, home address, or private detail about a person? Answer FAIL and quote it if so.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "23439e6c-c292-4035-adfe-7ed6b899e327",
              "leftValue": "={{ String($json.text ?? '').trim().toUpperCase().startsWith('PASS') }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "d624e109-cfa0-4612-a78b-3affeaa520f0",
      "name": "Passed check 3?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3340,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "errorMessage": "=Nothing sensitive is left in: did not pass, so the workflow stopped. {{ $(\"Check 3: Nothing sensitive is left in\").first().json.text }}"
      },
      "id": "55fe9e7d-9db6-4be4-abb4-6d1b9fc0a40a",
      "name": "Stop: check 3 failed",
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        3470,
        520
      ]
    },
    {
      "parameters": {
        "content": "**Check 4: check by hand**\nRead the final hand-over yourself, line by line, as if it might be forwarded. Is there anything in it you would not want a stranger to see?\n\nIf not: stop the run. This check is also listed in Result.",
        "height": 200,
        "width": 240,
        "color": 3
      },
      "id": "db496bc2-2be9-4d89-a883-33d03425f367",
      "name": "Check 4 (by hand)",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        3320,
        70
      ]
    },
    {
      "parameters": {
        "content": "**Check 5: check by hand**\nGive the final hand-over to someone who does not know the task, or read it as if you did not. Could they do the first three steps without asking you anything?\n\nIf not: flag it and carry on. This check is also listed in Result.",
        "height": 200,
        "width": 240,
        "color": 3
      },
      "id": "3cdf41cd-c950-4c2e-a654-c78b81caa50c",
      "name": "Check 5 (by hand)",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        3320,
        70
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "0e859823-da9d-4b1c-a6a1-d06171bc462a",
              "name": "result",
              "value": "={{ $(\"6. Close the gaps\").first().json.text }}",
              "type": "string"
            },
            {
              "id": "899f7948-6f7c-4bb5-a10a-873e16f8b853",
              "name": "flagged",
              "value": "={{ [$(\"Flag check 1\").isExecuted ? $(\"Flag check 1\").first().json.flag : \"\", $(\"Flag check 2\").isExecuted ? $(\"Flag check 2\").first().json.flag : \"\"].filter(x => x).join(\"\\n\") || \"None\" }}",
              "type": "string"
            },
            {
              "id": "35d87446-8ba4-434b-a0f4-b88c63be1909",
              "name": "check_by_hand",
              "value": "- Read the final hand-over yourself, line by line, as if it might be forwarded. Is there anything in it you would not want a stranger to see?\n- Give the final hand-over to someone who does not know the task, or read it as if you did not. Could they do the first three steps without asking you anything?",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "eff67a84-7f34-4251-ac8b-dfd268a6b64f",
      "name": "Result",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3600,
        320
      ],
      "notes": "The final answer, anything flagged, and the checks left for you.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "ebdb3586-cce7-4b87-aefd-539c1926c235",
      "name": "Chat model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        480,
        840
      ],
      "notes": "Attach your credential here, or swap in any chat model node.",
      "notesInFlow": true
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Inputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Inputs": {
      "main": [
        [
          {
            "node": "1. Sort the pieces",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. Sort the pieces": {
      "main": [
        [
          {
            "node": "Check 1: Each piece is hand over, keep or decide first",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 1: Each piece is hand over, keep or decide first": {
      "main": [
        [
          {
            "node": "Passed check 1?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 1?": {
      "main": [
        [
          {
            "node": "2. List what to have ready",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 1)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 1)": {
      "main": [
        [
          {
            "node": "1. Sort the pieces",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 1": {
      "main": [
        [
          {
            "node": "2. List what to have ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. List what to have ready": {
      "main": [
        [
          {
            "node": "3. Write the plain steps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. Write the plain steps": {
      "main": [
        [
          {
            "node": "4. Sweep for sensitive information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4. Sweep for sensitive information": {
      "main": [
        [
          {
            "node": "Check 2: The list of removed items is there",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 2: The list of removed items is there": {
      "main": [
        [
          {
            "node": "Passed check 2?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 2?": {
      "main": [
        [
          {
            "node": "5. Read it as someone new",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 2)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 2)": {
      "main": [
        [
          {
            "node": "4. Sweep for sensitive information",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 2": {
      "main": [
        [
          {
            "node": "5. Read it as someone new",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5. Read it as someone new": {
      "main": [
        [
          {
            "node": "6. Close the gaps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6. Close the gaps": {
      "main": [
        [
          {
            "node": "Check 3: Nothing sensitive is left in",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 3: Nothing sensitive is left in": {
      "main": [
        [
          {
            "node": "Passed check 3?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 3?": {
      "main": [
        [
          {
            "node": "Result",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Stop: check 3 failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chat model": {
      "ai_languageModel": [
        [
          {
            "node": "1. Sort the pieces",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "2. List what to have ready",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "3. Write the plain steps",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "4. Sweep for sensitive information",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "5. Read it as someone new",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "6. Close the gaps",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Check 3: Nothing sensitive is left in",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "template": "hand-off-a-task"
  }
}
