{
  "name": "Rewrite for a different reader",
  "nodes": [
    {
      "parameters": {
        "content": "## Rewrite for a different reader\nPull out the facts that must not change, work out what the new reader needs, rewrite the text for them, then check that every fact made it across and nothing new crept in.\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 quote in the Exact words in the original column appears word for word in the text as it is now. If it fails: try once more, then flag.\n- Check 2 (a plain rule, run automatically with no AI): Every row of the table has something written in \"What the reader needs\" and \"What that means for the new version\" (a blank or a dash does not count). If it fails: try once more, then flag.\n- Check 3 (a plain rule, run automatically with no AI): Every number in the answer also appears in the text as it is now. If it fails: try once more, then flag.\n- Check 4 (a plain rule, run automatically with no AI): Every row of the table has one of \"kept\", \"changed\" or \"missing\" in the In the new version column. If it fails: try once more, then flag.\n- Check 5 (a plain rule, run automatically with no AI): Every number in the answer also appears in the text as it is now. If it fails: try once more, then flag.\n- Check 6 (a second AI prompt answers pass or fail): Compare the final version with the facts below and with the original. Is every fact in the list still said, with the same meaning, and is there nothing in the final version that the original did not say? Answer FAIL and name the fact that changed or the line that was added. If it fails: try once more, then flag.\n- Check 7 (a second AI prompt answers pass or fail): Read the final version as the reader described below. Does it start with what they most need to know or do, and does it explain or avoid every word they would not know? Answer FAIL and quote the first sentence or word that does not fit. If it fails: try once more, then flag.\n- Check 8 (you check it by hand): Read the final version as the person it is for. Would they know what to do next, and does the tone feel right for them? Mark anything that would make them stop and ask, and fix it in your own words. If it fails: flag it and carry on.\n\nFrom Logic Lab (logiclabhq.com). Free to use and adapt, with credit.",
        "height": 800,
        "width": 760,
        "color": 5
      },
      "id": "ad9d108e-491c-4517-ad5d-e9c4a99e6d55",
      "name": "About this workflow",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -760
      ]
    },
    {
      "parameters": {},
      "id": "9e251042-4d6d-4967-a280-9d60bac294dd",
      "name": "Start",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        320
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "2fe5b4a8-1120-4b63-a8a3-e0da61a03a15",
              "name": "original",
              "value": "Sample: Phase 2 of the badge reader firmware rollout covers Building B and the annex. Patch 4.2 resolves the latency on the access controller but needs a controller reboot, so doors on the affected floors will fall back to the keypad override from 6:00 to 8:30 on Sunday, October 18. Badges issued before March will need re-enrollment at the front desk. Please direct queries to the facilities queue.",
              "type": "string"
            },
            {
              "id": "315f0adc-821c-41ef-a835-3deee77f1f31",
              "name": "reader",
              "value": "Sample: All office staff in Building B and the annex. Most are not technical. They want to know whether they will be locked out and what they have to do. They read on their phones, so keep it short.",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "30fc5327-19cd-4050-a330-7c69e1af61b2",
      "name": "Inputs",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        320
      ],
      "notes": "Paste your own material here: original = The text as it is now; reader = Who will read the new version.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a careful editor who protects the meaning of a text when it is rewritten.\n\nYour task is to list the facts in the original text that the new reader must not lose, meaning anything that would change what they understand or what they do if it were dropped or altered.\n\nThe original text:\n{{ $(\"Inputs\").first().json[\"original\"] }}\n\nWho will read the new version: {{ $(\"Inputs\").first().json[\"reader\"] }}\n\nRules:\n- Include the numbers, dates, times, names, places, amounts, decisions, deadlines, promises and instructions that this reader needs in order to understand the message or act on it.\n- Leave out detail this reader does not need, such as internal reference numbers.\n- Copy the exact words from the original into the last column, word for word. Do not fix or shorten them.\n- Write each fact as one short line. List 4 to 12 facts. Do not add anything that is not in the original.\n\nReturn one markdown table and nothing else:\n| # | Fact | Exact words in the original |{{ $(\"Check 1: Every quote is really in the original\").isExecuted ? \"\\n\\nYour last answer did not pass this check: Every quote is really in the original. What was wrong: \" + $(\"Check 1: Every quote is really in the original\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "e712f512-1628-4e39-a1bc-47d881f87b9f",
      "name": "1. List the facts that must stay",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        480,
        320
      ],
      "notes": "Pick out the facts in the original that this reader must not lose, quoting the exact words. Returns: A numbered table of the facts that must stay, each with its exact words from the original.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// Every quote is really in the original\n// Rule: Every quote in the Exact words in the original column appears word for word in the text as it is now.\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. List the facts that must stay\").first().json.text ?? '');\nconst sources = {\n  \"original\": String($(\"Inputs\").first().json[\"original\"] ?? ''),\n};\nconst result = runRule(\"column_quotes_source\", {\"column\":\"Exact words in the original\",\"input\":\"original\"}, work, sources);\nreturn [{ json: result }];"
      },
      "id": "3bb116aa-2af3-4635-a532-debcd766f1f7",
      "name": "Check 1: Every quote is really in the original",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        740,
        320
      ],
      "notes": "Every quote in the Exact words in the original column appears word for word in the text as it is now.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "56f9e471-1ef6-4f1a-a5b2-d4b39b969dac",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "024d1f7a-9f1f-460f-ab23-03d043476a55",
      "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": "637f666e-0e5e-4935-ac0d-e60089b27c57",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "9e417ac2-8089-4125-af1a-8ef867390ac3",
      "name": "Try again? (check 1)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1000,
        520
      ],
      "notes": "True = first failure: send back to \"1. List the facts that must stay\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "4f0f6553-2921-4a20-a3dc-e8d1e180bfc6",
              "name": "flag",
              "value": "=Every quote is really in the original: did not pass. {{ $(\"Check 1: Every quote is really in the original\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "efabe8e7-9fd4-4094-a59c-2135001fd682",
      "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 experienced communicator who writes for many different kinds of readers.\n\nYour task is to say what this reader needs from the new version, so that the rewrite is built around them and not around the original.\n\nWho will read the new version: {{ $(\"Inputs\").first().json[\"reader\"] }}\n\nThe original text:\n{{ $(\"Inputs\").first().json[\"original\"] }}\n\nThe facts that must stay:\n{{ $(\"1. List the facts that must stay\").first().json.text }}\n\nRules:\n- Write 4 to 6 rows. Cover what the reader already knows, which words in the original they would not know and what to say instead, what they should read first, what they can be spared, and what they should do next.\n- Base every row on the description of the reader. If the description does not say, write \"not stated\" in the second column. Do not guess.\n- Do not change the list of facts or add to it.\n\nReturn one markdown table and nothing else:\n| # | What the reader needs | What that means for the new version |{{ $(\"Check 2: Every row has something written in both columns\").isExecuted ? \"\\n\\nYour last answer did not pass this check: Every row has something written in both columns. What was wrong: \" + $(\"Check 2: Every row has something written in both columns\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "1ee3b807-d4c7-4912-a02e-c191ddd93aa4",
      "name": "2. Say what this reader needs",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        1260,
        320
      ],
      "notes": "Describe what the new reader already knows, what to explain, what to put first and what to spare them. Returns: A short table of what this reader needs and what that means for the rewrite.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// Every row has something written in both columns\n// Rule: Every row of the table has something written in \"What the reader needs\" and \"What that means for the new version\" (a blank or a dash does not count).\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($(\"2. Say what this reader needs\").first().json.text ?? '');\nconst sources = {\n\n};\nconst result = runRule(\"table_cells_filled\", [\"What the reader needs\",\"What that means for the new version\"], work, sources);\nreturn [{ json: result }];"
      },
      "id": "59e5777f-a97e-40d0-a377-69f582a9eefe",
      "name": "Check 2: Every row has something written in both columns",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1520,
        320
      ],
      "notes": "Every row of the table has something written in \"What the reader needs\" and \"What that means for the new version\" (a blank or a dash does not count).",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "55f9e2de-1ff6-40ad-a4b2-d3209c969f3f",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "82459a35-9f1c-4778-ab25-4267c34eef9a",
      "name": "Passed check 2?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1780,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "647f6801-0d5e-47a2-ad0d-e79388b27ac4",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "9643acc1-1886-4ed6-af13-09b3e7409008",
      "name": "Try again? (check 2)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1780,
        520
      ],
      "notes": "True = first failure: send back to \"2. Say what this reader needs\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "4e0f63c0-2a21-4bb3-a2dc-e73ee280c159",
              "name": "flag",
              "value": "=Every row has something written in both columns: did not pass. {{ $(\"Check 2: Every row has something written in both columns\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "f0abea7a-a2d4-454d-a29c-1c7cff1fd4ef",
      "name": "Flag check 2",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1910,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a skilled writer who can say the same thing to different people in the words that suit each of them.\n\nYour task is to rewrite the original text for the reader described below.\n\nThe original text:\n{{ $(\"Inputs\").first().json[\"original\"] }}\n\nWho will read the new version: {{ $(\"Inputs\").first().json[\"reader\"] }}\n\nWhat the reader needs:\n{{ $(\"2. Say what this reader needs\").first().json.text }}\n\nThe facts that must stay:\n{{ $(\"1. List the facts that must stay\").first().json.text }}\n\nRules:\n- Put first what the reader most needs to know or do.\n- Keep every fact in the list, with the same meaning. Write every number, date and time exactly as the original writes it, for example 15:00 and not 3 pm, so it can be matched.\n- Replace or explain words this reader would not know. Cut detail they do not need.\n- Match the length and tone the reader needs. Do not add facts, promises, reasons or advice that are not in the original.\n- Do not mention the original, the rewrite or what you changed.\n\nReturn the new version and nothing else.{{ $(\"Check 3: No number was added or changed\").isExecuted ? \"\\n\\nYour last answer did not pass this check: No number was added or changed. What was wrong: \" + $(\"Check 3: No number was added or changed\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "e270eab5-f45d-4e2e-a448-d32ba85df41c",
      "name": "3. Write the new version",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        2040,
        320
      ],
      "notes": "Rewrite the text for this reader, keeping every fact and writing numbers exactly as they were. Returns: The text rewritten for the new reader.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// No number was added or changed\n// Rule: Every number in the answer also appears in the text as it is now.\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($(\"3. Write the new version\").first().json.text ?? '');\nconst sources = {\n  \"original\": String($(\"Inputs\").first().json[\"original\"] ?? ''),\n};\nconst result = runRule(\"numbers_in_source\", {\"input\":\"original\"}, work, sources);\nreturn [{ json: result }];"
      },
      "id": "d0197e2d-f391-47fc-a511-177fb5c8e6be",
      "name": "Check 3: No number was added or changed",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2300,
        320
      ],
      "notes": "Every number in the answer also appears in the text as it is now.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "54f9e14b-1cf6-4bf4-a7b2-d7d99d96a0d2",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "8247d8cc-9f1a-48e1-ab27-80fe434be783",
      "name": "Passed check 3?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2560,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "617f6348-0c5e-460f-ae0d-e9268bb27f7d",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "fa4688c4-f484-4793-a315-80f6f33e6455",
      "name": "Try again? (check 3)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2560,
        520
      ],
      "notes": "True = first failure: send back to \"3. Write the new version\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "510f6879-2b21-4d46-a1dc-e5abdf80bca0",
              "name": "flag",
              "value": "=No number was added or changed: did not pass. {{ $(\"Check 3: No number was added or changed\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "f1abec0d-a1d4-43ba-a39c-1e0ffe1fd35c",
      "name": "Flag check 3",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2690,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a meticulous proofreader who checks a rewrite against its source.\n\nYour task is to check, for every fact in the list, whether the new version still says it.\n\nThe facts that must stay:\n{{ $(\"1. List the facts that must stay\").first().json.text }}\n\nThe new version:\n{{ $(\"3. Write the new version\").first().json.text }}\n\nRules:\n- Mark a fact \"kept\" if the new version says it with the same meaning, \"changed\" if it says something different or less exact, and \"missing\" if it is not there.\n- Be strict. A date that has moved, a number that has been rounded, or a \"must\" that has become a \"may\" is \"changed\".\n- In the last column, copy the words from the new version that carry the fact. Write \"none\" if the fact is missing.\n- Do not rewrite anything. Do not add facts to the list.\n\nReturn one markdown table and nothing else:\n| # | Fact | In the new version | Words in the new version |\nThe third column must say exactly kept, changed or missing.{{ $(\"Check 4: Each fact is marked kept, changed or missing\").isExecuted ? \"\\n\\nYour last answer did not pass this check: Each fact is marked kept, changed or missing. What was wrong: \" + $(\"Check 4: Each fact is marked kept, changed or missing\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "25dad993-b8a1-431c-a003-50296f3868f2",
      "name": "4. Check each fact against the new version",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        2820,
        320
      ],
      "notes": "Go through the facts one by one and mark each as kept, changed or missing in the new version. Returns: A table that marks each fact as kept, changed or missing in the new version.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// Each fact is marked kept, changed or missing\n// Rule: Every row of the table has one of \"kept\", \"changed\" or \"missing\" in the In the new version 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($(\"4. Check each fact against the new version\").first().json.text ?? '');\nconst sources = {\n\n};\nconst result = runRule(\"column_values_in\", {\"column\":\"In the new version\",\"allowed\":[\"kept\",\"changed\",\"missing\"]}, work, sources);\nreturn [{ json: result }];"
      },
      "id": "7b42700e-43bb-456f-aec0-ab34e2404ab5",
      "name": "Check 4: Each fact is marked kept, changed or missing",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3080,
        320
      ],
      "notes": "Every row of the table has one of \"kept\", \"changed\" or \"missing\" in the In the new version column.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "53f9dfb8-1df6-4d87-a6b2-d6469e96a265",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "8254a4bf-1f17-40ca-ab16-37ddc3536cc8",
      "name": "Passed check 4?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3340,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "627f64db-0b5e-447c-af0d-eab98ab27dea",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "f235330b-fc95-4d4c-a321-83697345e99a",
      "name": "Try again? (check 4)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3340,
        520
      ],
      "notes": "True = first failure: send back to \"4. Check each fact against the new version\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "500f66e6-2c21-4ed9-a0dc-e418e080be33",
              "name": "flag",
              "value": "=Each fact is marked kept, changed or missing: did not pass. {{ $(\"Check 4: Each fact is marked kept, changed or missing\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "eaabe108-9cd4-4bdb-a09c-1956fd1fd1c9",
      "name": "Flag check 4",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3470,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "promptType": "define",
        "hasOutputParser": false,
        "text": "=You are a skilled writer who finishes a rewrite carefully.\n\nYour task is to produce the final version of the text for this reader, fixing every fact that the check marked \"changed\" or \"missing\" and leaving the rest as it is.\n\nThe new version:\n{{ $(\"3. Write the new version\").first().json.text }}\n\nThe check of each fact:\n{{ $(\"4. Check each fact against the new version\").first().json.text }}\n\nThe facts that must stay:\n{{ $(\"1. List the facts that must stay\").first().json.text }}\n\nWho will read it: {{ $(\"Inputs\").first().json[\"reader\"] }}\n\nRules:\n- Change only what is needed to put each \"changed\" or \"missing\" fact back as the list of facts has it. If every fact is \"kept\", return the new version exactly as it is.\n- Write numbers, dates and times exactly as the facts list does. Do not add anything new.\n- Do not add notes, headings about the changes or a list of what you fixed.\n\nReturn the final version and nothing else.{{ $(\"Check 5: No number in the final version is new\").isExecuted ? \"\\n\\nYour last answer did not pass this check: No number in the final version is new. What was wrong: \" + $(\"Check 5: No number in the final version is new\").first().json.reason + \"\\nFix that and give your full answer again.\" : \"\" }}{{ $(\"Check 6: Every fact is still said and nothing was added\").isExecuted ? \"\\n\\nYour last answer did not pass this check: Every fact is still said and nothing was added. What was wrong: \" + $(\"Check 6: Every fact is still said and nothing was added\").first().json.text + \"\\nFix that and give your full answer again.\" : \"\" }}{{ $(\"Check 7: It starts with what the reader needs and avoids words they do not know\").isExecuted ? \"\\n\\nYour last answer did not pass this check: It starts with what the reader needs and avoids words they do not know. What was wrong: \" + $(\"Check 7: It starts with what the reader needs and avoids words they do not know\").first().json.text + \"\\nFix that and give your full answer again.\" : \"\" }}"
      },
      "id": "fd33fa9c-6d95-4581-adec-e3328eac3a0f",
      "name": "5. Put back anything that changed",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        3600,
        320
      ],
      "notes": "Fix every fact marked changed or missing, and leave the rest of the new version as it is. Returns: The final text for the new reader, with every fact back as the original had it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "jsCode": "// No number in the final version is new\n// Rule: Every number in the answer also appears in the text as it is now.\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($(\"5. Put back anything that changed\").first().json.text ?? '');\nconst sources = {\n  \"original\": String($(\"Inputs\").first().json[\"original\"] ?? ''),\n};\nconst result = runRule(\"numbers_in_source\", {\"input\":\"original\"}, work, sources);\nreturn [{ json: result }];"
      },
      "id": "0b1ac1d3-add2-46e4-a17c-82fde51ceade",
      "name": "Check 5: No number in the final version is new",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3860,
        320
      ],
      "notes": "Every number in the answer also appears in the text as it is now.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "5af9eabd-1af6-48ce-a9b2-daff97969760",
              "leftValue": "={{ $json.pass === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "8256e356-1f15-4233-ab19-3ff4c3512e31",
      "name": "Passed check 5?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4120,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "677f6cba-0a5e-42e9-a00d-ec4c85b2760b",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "1637aa4e-1893-4ac9-a724-5f6c4f437257",
      "name": "Try again? (check 5)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4120,
        520
      ],
      "notes": "True = first failure: send back to \"5. Put back anything that changed\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "530f6b9f-2d21-406c-a7dc-ef1de580c612",
              "name": "flag",
              "value": "=No number in the final version is new: did not pass. {{ $(\"Check 5: No number in the final version is new\").first().json.reason }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "ebabe29b-9bd4-4a48-a19c-1ae9fc1fd036",
      "name": "Flag check 5",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        4250,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "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: Every fact is still said and nothing was added\nCompare the final version with the facts below and with the original. Is every fact in the list still said, with the same meaning, and is there nothing in the final version that the original did not say? Answer FAIL and name the fact that changed or the line that was added.\n\nThe facts that must stay:\n{{ $(\"1. List the facts that must stay\").first().json.text }}\n\nThe original text:\n{{ $(\"Inputs\").first().json[\"original\"] }}\n\nThe work to check (the answer from Step 5 (Put back anything that changed)):\n{{ $(\"5. Put back anything that changed\").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": "cfd3e375-88de-49e0-a7cd-1c7fd3408d0a",
      "name": "Check 6: Every fact is still said and nothing was added",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        4380,
        320
      ],
      "notes": "Answers PASS or FAIL. Compare the final version with the facts below and with the original. Is every fact in the list still said, with the same meaning, and is there nothing in the final version that the original did not say? Answer FAIL and name the fact that changed or the line that was added.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "59f9e92a-1bf6-4a61-a8b2-d96c989698f3",
              "leftValue": "={{ String($json.text ?? '').trim().toUpperCase().startsWith('PASS') }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "024f5e11-1f13-439c-ab1b-7e8b4358b376",
      "name": "Passed check 6?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4640,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "687f6e4d-095e-4156-a10d-eddf84b27478",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "fe39c31d-0091-466a-a71d-0c876f4a607c",
      "name": "Try again? (check 6)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4640,
        520
      ],
      "notes": "True = first failure: send back to \"5. Put back anything that changed\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "520f6a0c-2e21-41ff-a6dc-ed8ae680c7a5",
              "name": "flag",
              "value": "=Every fact is still said and nothing was added: did not pass. {{ $(\"Check 6: Every fact is still said and nothing was added\").first().json.text }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "ecabe42e-9ed4-4f01-ae9c-1630fb1fcea3",
      "name": "Flag check 6",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        4770,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "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: It starts with what the reader needs and avoids words they do not know\nRead the final version as the reader described below. Does it start with what they most need to know or do, and does it explain or avoid every word they would not know? Answer FAIL and quote the first sentence or word that does not fit.\n\nThe reader:\n{{ $(\"Inputs\").first().json[\"reader\"] }}\n\nThe work to check (the answer from Step 5 (Put back anything that changed)):\n{{ $(\"5. Put back anything that changed\").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": "cc71ff2f-9a04-447e-a64d-bfb1ff0bb6f0",
      "name": "Check 7: It starts with what the reader needs and avoids words they do not know",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        4900,
        320
      ],
      "notes": "Answers PASS or FAIL. Read the final version as the reader described below. Does it start with what they most need to know or do, and does it explain or avoid every word they would not know? Answer FAIL and quote the first sentence or word that does not fit.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "58f9e797-18f6-45a8-abb2-de2599969a86",
              "leftValue": "={{ String($json.text ?? '').trim().toUpperCase().startsWith('PASS') }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "82526628-9f10-4b85-ab1d-bd22c355ab5f",
      "name": "Passed check 7?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        5160,
        320
      ],
      "notes": "True = passed. False = did not pass.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "657f6994-085e-4fc3-a20d-ef7287b27931",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "123c2130-9c8e-4a67-ab1f-1f0a4b47e939",
      "name": "Try again? (check 7)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        5160,
        520
      ],
      "notes": "True = first failure: send back to \"5. Put back anything that changed\" once. False = failed twice: flag it.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "550f6ec5-2f21-4392-a5dc-ebf7e380c2ec",
              "name": "flag",
              "value": "=It starts with what the reader needs and avoids words they do not know: did not pass. {{ $(\"Check 7: It starts with what the reader needs and avoids words they do not know\").first().json.text }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "edabe5c1-9dd4-4d6e-af9c-17c3fa1fcd10",
      "name": "Flag check 7",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        5290,
        700
      ],
      "notes": "Notes the problem and carries on. The note shows in Result.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "content": "**Check 8: check by hand**\nRead the final version as the person it is for. Would they know what to do next, and does the tone feel right for them? Mark anything that would make them stop and ask, and fix it in your own words.\n\nIf not: flag it and carry on. This check is also listed in Result.",
        "height": 200,
        "width": 240,
        "color": 3
      },
      "id": "359e4f47-b6dd-42ae-a299-7699c05a97b0",
      "name": "Check 8 (by hand)",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        5140,
        70
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "90fb1b16-b1e9-44db-a372-e3984aba5255",
              "name": "result",
              "value": "={{ $(\"5. Put back anything that changed\").first().json.text }}",
              "type": "string"
            },
            {
              "id": "575cf317-9626-4ed0-acc0-2f2911af06ca",
              "name": "flagged",
              "value": "={{ [$(\"Flag check 1\").isExecuted ? $(\"Flag check 1\").first().json.flag : \"\", $(\"Flag check 2\").isExecuted ? $(\"Flag check 2\").first().json.flag : \"\", $(\"Flag check 3\").isExecuted ? $(\"Flag check 3\").first().json.flag : \"\", $(\"Flag check 4\").isExecuted ? $(\"Flag check 4\").first().json.flag : \"\", $(\"Flag check 5\").isExecuted ? $(\"Flag check 5\").first().json.flag : \"\", $(\"Flag check 6\").isExecuted ? $(\"Flag check 6\").first().json.flag : \"\", $(\"Flag check 7\").isExecuted ? $(\"Flag check 7\").first().json.flag : \"\"].filter(x => x).join(\"\\n\") || \"None\" }}",
              "type": "string"
            },
            {
              "id": "ba6efa2d-f6f5-44ae-ac50-896fa64929b8",
              "name": "check_by_hand",
              "value": "- Read the final version as the person it is for. Would they know what to do next, and does the tone feel right for them? Mark anything that would make them stop and ask, and fix it in your own words.",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "af1ae0c3-9063-4b6c-a2cd-c515d50e3ace",
      "name": "Result",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        5420,
        320
      ],
      "notes": "The final answer, anything flagged, and the checks left for you.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "11b821b9-7c96-4ade-a2cd-3e7be41d5208",
      "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. List the facts that must stay",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. List the facts that must stay": {
      "main": [
        [
          {
            "node": "Check 1: Every quote is really in the original",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 1: Every quote is really in the original": {
      "main": [
        [
          {
            "node": "Passed check 1?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 1?": {
      "main": [
        [
          {
            "node": "2. Say what this reader needs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 1)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 1)": {
      "main": [
        [
          {
            "node": "1. List the facts that must stay",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 1": {
      "main": [
        [
          {
            "node": "2. Say what this reader needs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. Say what this reader needs": {
      "main": [
        [
          {
            "node": "Check 2: Every row has something written in both columns",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 2: Every row has something written in both columns": {
      "main": [
        [
          {
            "node": "Passed check 2?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 2?": {
      "main": [
        [
          {
            "node": "3. Write the new version",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 2)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 2)": {
      "main": [
        [
          {
            "node": "2. Say what this reader needs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 2": {
      "main": [
        [
          {
            "node": "3. Write the new version",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. Write the new version": {
      "main": [
        [
          {
            "node": "Check 3: No number was added or changed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 3: No number was added or changed": {
      "main": [
        [
          {
            "node": "Passed check 3?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 3?": {
      "main": [
        [
          {
            "node": "4. Check each fact against the new version",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 3)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 3)": {
      "main": [
        [
          {
            "node": "3. Write the new version",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 3": {
      "main": [
        [
          {
            "node": "4. Check each fact against the new version",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4. Check each fact against the new version": {
      "main": [
        [
          {
            "node": "Check 4: Each fact is marked kept, changed or missing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 4: Each fact is marked kept, changed or missing": {
      "main": [
        [
          {
            "node": "Passed check 4?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 4?": {
      "main": [
        [
          {
            "node": "5. Put back anything that changed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 4)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 4)": {
      "main": [
        [
          {
            "node": "4. Check each fact against the new version",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 4": {
      "main": [
        [
          {
            "node": "5. Put back anything that changed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5. Put back anything that changed": {
      "main": [
        [
          {
            "node": "Check 5: No number in the final version is new",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 5: No number in the final version is new": {
      "main": [
        [
          {
            "node": "Passed check 5?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 5?": {
      "main": [
        [
          {
            "node": "Check 6: Every fact is still said and nothing was added",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 5)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 5)": {
      "main": [
        [
          {
            "node": "5. Put back anything that changed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 5": {
      "main": [
        [
          {
            "node": "Check 6: Every fact is still said and nothing was added",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 6: Every fact is still said and nothing was added": {
      "main": [
        [
          {
            "node": "Passed check 6?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 6?": {
      "main": [
        [
          {
            "node": "Check 7: It starts with what the reader needs and avoids words they do not know",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 6)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 6)": {
      "main": [
        [
          {
            "node": "5. Put back anything that changed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 6": {
      "main": [
        [
          {
            "node": "Check 7: It starts with what the reader needs and avoids words they do not know",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check 7: It starts with what the reader needs and avoids words they do not know": {
      "main": [
        [
          {
            "node": "Passed check 7?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Passed check 7?": {
      "main": [
        [
          {
            "node": "Result",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Try again? (check 7)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Try again? (check 7)": {
      "main": [
        [
          {
            "node": "5. Put back anything that changed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Flag check 7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag check 7": {
      "main": [
        [
          {
            "node": "Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chat model": {
      "ai_languageModel": [
        [
          {
            "node": "1. List the facts that must stay",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "2. Say what this reader needs",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "3. Write the new version",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "4. Check each fact against the new version",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "5. Put back anything that changed",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Check 6: Every fact is still said and nothing was added",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Check 7: It starts with what the reader needs and avoids words they do not know",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "template": "rewrite-for-a-different-reader"
  }
}
