{
  "name": "DEV_RIO_702_X_Post_Prepare",
  "nodes": [
    {
      "parameters": {},
      "id": "b1a10702-0000-4000-8000-000000000001",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -848,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// DEV_RIO_702 X(Twitter)\u6295\u7a3f\u3010\u4e0b\u66f8\u304d\u6e96\u5099\u306e\u307f\u30fbdry-run\u3011\u3002\n// DEV_RIO_103 \u306e\u51fa\u529b\u3092\u53d7\u3051\u53d6\u308b\u3002103\u306b\u306f title \u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u7121\u3044\u305f\u3081 theme \u3092\u8a71\u984c\u30d2\u30f3\u30c8\u306b\u4f7f\u3046\u3002\n// qa_status \u304c\u7121\u3044\u624b\u52d5\u30c6\u30b9\u30c8\u6642\u306f 'PASS' \u6271\u3044\uff08\u5b9f\u30c1\u30a7\u30a4\u30f3\u3067\u306f103\u304c\u5fc5\u305a\u8a2d\u5b9a\u3059\u308b\uff09\u3002\nconst i = $input.first().json;\nreturn [{ json: {\n  content_id: i.content_id ?? 'ca-x-001',\n  theme: i.theme ?? '\uff08\u30c6\u30fc\u30de\u672a\u8a2d\u5b9a\uff09',\n  note_body: i.note_body ?? i.note_draft_body ?? '',\n  threads_post: i.threads_post ?? '',\n  qa_status: i.qa_status ?? 'PASS',\n  platform: 'x',\n  action_type: 'prepare_x_drafts'\n} }];"
      },
      "id": "b1a10702-0000-4000-8000-000000000002",
      "name": "Collect Inputs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -624,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "gate-qa-pass",
              "leftValue": "={{ $json.qa_status }}",
              "rightValue": "PASS",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b1a10702-0000-4000-8000-000000000003",
      "name": "QA Gate (PASS only)",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -400,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// X\u6295\u7a3f\u306e\u4e0b\u66f8\u304d\u751f\u6210\u30d7\u30ed\u30f3\u30d7\u30c8\u3092 Anthropic body \u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3068\u3057\u3066\u7d44\u307f\u7acb\u3066\u308b\u3002\n// \u30c6\u30ad\u30b9\u30c8\u306fJS\u6587\u5b57\u5217\u306b\u5b89\u5168\u306b\u57cb\u3081\u8fbc\u3080\uff08jsonBody\u3078\u306e\u76f4\u63a5\u6587\u5b57\u5217\u88dc\u9593\u306f\u58ca\u308c\u3084\u3059\u3044\u305f\u3081\u56de\u907f\uff09\u3002\nconst i = $input.first().json;\nconst excerpt = String(i.note_body || '').slice(0, 500);\nconst prompt = `\u4ee5\u4e0b\u306e\u8a18\u4e8b\u304b\u3089\u3001X(Twitter)\u6295\u7a3f\u306e\u4e0b\u66f8\u304d\u30923\u30d1\u30bf\u30fc\u30f3\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002\\n\\n\u30c6\u30fc\u30de: ${i.theme}\\n\u672c\u6587\u629c\u7c8b: ${excerpt}\\n\\n\u6761\u4ef6:\\n- \u5404140\u5b57\u4ee5\u5185\u3001\u65e5\u672c\u8a9e\u3002\\n- \u30cf\u30c3\u30b7\u30e5\u30bf\u30b02\u301c3\u500b\u3002\\n- \u8a87\u5f35\u30fb\u865a\u507d\u306e\u6570\u5024\u30fb\u65ad\u5b9a\u7684\u306a\u6210\u679c\u4fdd\u8a3c\u306f\u7981\u6b62\u3002\\n- \u51fa\u529b\u306f\u6b21\u306eJSON\u914d\u5217\u306e\u307f\uff08\u524d\u5f8c\u306b\u8aac\u660e\u30fb\u30b3\u30fc\u30c9\u30d6\u30ed\u30c3\u30af\u8a18\u53f7\u3092\u4ed8\u3051\u306a\u3044\uff09:\\n[{\"angle\":\"string\",\"text\":\"string\"}]`;\nconst anthropic_body = {\n  model: 'claude-haiku-4-5',\n  max_tokens: 1024,\n  messages: [ { role: 'user', content: prompt } ]\n};\nreturn [{ json: { ...i, anthropic_body } }];"
      },
      "id": "b1a10702-0000-4000-8000-000000000004",
      "name": "Build X Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -176,
        -112
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "anthropicApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.anthropic_body }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "b1a10702-0000-4000-8000-000000000005",
      "name": "Generate X Drafts (Claude)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        48,
        -112
      ],
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "jsCode": "// Claude\u5fdc\u7b54\u3092\u30d1\u30fc\u30b9\u3002\u5931\u6557\u6642\u306f\u634f\u9020\u305b\u305a parse_error \u3092\u660e\u793a\u3002\u51aa\u7b49\u6027\u30ad\u30fc\u4ed8\u4e0e\u3002\u516c\u958b\u76f4\u524d\u3067\u505c\u6b62\u3002\nconst input = $input.first().json;\nconst ctx = $('Collect Inputs').first().json;\nlet drafts = [];\nlet parse_error = null;\nif (input.error) {\n  parse_error = 'anthropic_api_error: ' + JSON.stringify(input.error).slice(0, 300);\n} else {\n  try {\n    const text = input.content && input.content[0] && input.content[0].text;\n    if (!text) throw new Error('no text in response');\n    const cleaned = text.trim().replace(/^```json\\s*/i, '').replace(/```\\s*$/, '');\n    const match = cleaned.match(/\\[[\\s\\S]*\\]/);\n    drafts = JSON.parse(match ? match[0] : cleaned);\n    if (!Array.isArray(drafts) || drafts.length === 0) throw new Error('drafts missing/empty');\n    drafts = drafts.map(d => ({ angle: d.angle ?? '', text: d.text ?? '' }));\n  } catch (e) {\n    parse_error = 'parse_failed: ' + e.message;\n  }\n}\nif (parse_error) drafts = [{ angle: 'manual', text: '\uff08AI\u5fdc\u7b54\u306e\u53d6\u5f97/\u89e3\u6790\u306b\u5931\u6557\u3002\u624b\u52d5\u3067\u4e0b\u66f8\u304d\u3092\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\uff09' }];\nconst idempotency_key = [ctx.platform, ctx.content_id, ctx.action_type].join('|');\nreturn [{ json: {\n  content_id: ctx.content_id,\n  platform: 'x',\n  drafts,\n  draft_count: drafts.length,\n  idempotency_key,\n  state: 'WAITING_APPROVAL',\n  published: false,\n  draft_error: parse_error,\n  note: 'dry-run\u3002X\u6295\u7a3f\u306e\u4e0b\u66f8\u304d\u5019\u88dc\u306e\u307f\u751f\u6210\u3002\u2605\u81ea\u52d5\u6295\u7a3f\u306f\u3057\u306a\u3044\u3002\u4eba\u9593\u304c\u5185\u5bb9\u3092\u78ba\u8a8d\u3057\u3001\u554f\u984c\u306a\u3051\u308c\u3070\u624b\u52d5\u3067X\u306b\u6295\u7a3f\u3059\u308b\uff08CLAUDE.md \u6e96\u62e0\uff09\u3002'\n} }];"
      },
      "id": "b1a10702-0000-4000-8000-000000000006",
      "name": "Stop Before Publish",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        272,
        -112
      ]
    },
    {
      "parameters": {},
      "id": "b1a10702-0000-4000-8000-000000000007",
      "name": "Result (drafts only, no publish)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        496,
        -112
      ]
    },
    {
      "parameters": {
        "jsCode": "// QA\u30b2\u30fc\u30c8\u4e0d\u901a\u904e\uff08qa_status != PASS\uff09\u3002\u4e0b\u66f8\u304d\u751f\u6210\u3092\u30b9\u30ad\u30c3\u30d7\u3002\nconst i = $input.first().json;\nreturn [{ json: {\n  content_id: i.content_id,\n  platform: 'x',\n  skipped: true,\n  qa_status: i.qa_status,\n  state: 'SKIPPED_NOT_PASS',\n  published: false,\n  note: 'qa_status \u304c PASS \u3067\u306f\u306a\u3044\u305f\u3081 X\u4e0b\u66f8\u304d\u751f\u6210\u3092\u30b9\u30ad\u30c3\u30d7\uff08\u54c1\u8cea\u672a\u9054\u30b3\u30f3\u30c6\u30f3\u30c4\u306f\u914d\u5e03\u6e96\u5099\u3057\u306a\u3044\uff09\u3002'\n} }];"
      },
      "id": "b1a10702-0000-4000-8000-000000000008",
      "name": "Skip (not PASS)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -176,
        176
      ]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Collect Inputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Inputs": {
      "main": [
        [
          {
            "node": "QA Gate (PASS only)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "QA Gate (PASS only)": {
      "main": [
        [
          {
            "node": "Build X Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Skip (not PASS)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build X Prompt": {
      "main": [
        [
          {
            "node": "Generate X Drafts (Claude)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate X Drafts (Claude)": {
      "main": [
        [
          {
            "node": "Stop Before Publish",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stop Before Publish": {
      "main": [
        [
          {
            "node": "Result (drafts only, no publish)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "meta": {
    "description": "DEV_RIO_702 X Post Prepare. dry-run. qa_status=PASS \u306e\u3068\u304d\u306e\u307f Claude \u3067 X\u6295\u7a3f\u4e0b\u66f8\u304d3\u6848\u3092\u751f\u6210\u3057\u516c\u958b\u76f4\u524d\u3067\u505c\u6b62\u3002NEVER publish/activate. \u81ea\u52d5\u6295\u7a3f\u306f\u3057\u306a\u3044\uff08CLAUDE.md \u6e96\u62e0\uff09\u3002\u4eba\u9593\u304c\u78ba\u8a8d\u5f8c\u3001\u624b\u52d5\u6295\u7a3f\u3002"
  },
  "tags": [
    {
      "name": "DEV"
    },
    {
      "name": "RIO"
    },
    {
      "name": "X"
    }
  ]
}