{
  "name": "Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix",
  "nodes": [
    {
      "parameters": {
        "path": "echo-brand-voice-trigger",
        "formTitle": "Echo Brand Voice Analysis",
        "formDescription": "Analyze writing samples to create a brand voice profile for AI agents.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Voice Type",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "personal"
                  },
                  {
                    "option": "company"
                  },
                  {
                    "option": "combined"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Person Name",
              "placeholder": "e.g., Tyler Fisk (required for personal/combined)"
            },
            {
              "fieldLabel": "Company Name",
              "placeholder": "e.g., Hattie B's (required for company/combined)"
            },
            {
              "fieldLabel": "Writing Samples",
              "fieldType": "textarea",
              "placeholder": "Paste writing samples here (emails, transcripts, social posts). More samples = better analysis.",
              "requiredField": true
            },
            {
              "fieldLabel": "Company Materials",
              "fieldType": "textarea",
              "placeholder": "For company/combined: Paste brand guidelines, style guide, or marketing copy."
            },
            {
              "fieldLabel": "Skip QC Validation",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "No (Recommended)"
                  },
                  {
                    "option": "Yes (Save ~$0.03)"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "id": "8ecc78ac-790e-4c44-8360-175e04a96ea2",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.1,
      "position": [
        -1104,
        1056
      ],
      "notes": "Entry point for brand voice analysis.\n\nVoice Types:\n- personal: Analyze individual's voice\n- company: Analyze company brand voice\n- combined: Personal voice at company context"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "claude-key",
              "name": "claude_api_key",
              "value": "REPLACE_WITH_YOUR_ANTHROPIC_API_KEY",
              "type": "string"
            },
            {
              "id": "analyzer-prompt",
              "name": "analyzer_prompt",
              "value": "See assets/prompt-library/agents/echo-analyzer.md for full prompt",
              "type": "string"
            },
            {
              "id": "formatter-prompt",
              "name": "formatter_prompt",
              "value": "<!-- Echo Brand Voice Formatter v1.0 -->\n<version_info>\n<name>Echo - Brand Voice Formatter</name>\n<version>1.0</version>\n<date>2025-11-27</date>\n<phase>3 (XML Generation)</phase>\n</version_info>\n\n<role>\nYou are a precision formatter that transforms JSON brand voice analysis into optimized XML snippets. Your output will be injected directly into AI agent system prompts, so every word must serve a purpose.\n\nYou prioritize:\n- Token efficiency (\u22641000 words strict limit)\n- Actionable specificity (AI can implement your guidelines)\n- Structured consistency (XML schema must be followed exactly)\n</role>\n\n<input_format>\nYou will receive a JSON object from the Echo Analyzer containing:\n- voice_type, subject information\n- analysis_summary\n- linguistic_profile\n- vocabulary_compilation\n- dos_and_donts\n- emotional_range\n- contextual_variations\n- golden_threads\n- quantitative_anchors\n- conflict_resolutions (for combined type only)\n</input_format>\n\n<output_schema>\n## Required XML Structure\n\nYour output MUST follow this exact schema:\n\n<BrandVoice type=\"[personal|company|combined]\" subject=\"[Name]\" company=\"[Company or null]\">\n\n  <summary>\n    [100-150 word essence of this voice. Must be specific enough that\n    reading this alone gives a clear sense of the voice character.]\n  </summary>\n\n  <golden_threads>\n    <thread priority=\"1\">[Core motivation - weave throughout content]</thread>\n    <thread priority=\"2\">[Secondary motivation]</thread>\n    <thread priority=\"3\">[Tertiary motivation if applicable]</thread>\n  </golden_threads>\n\n  <key_characteristics>\n    <characteristic>\n      <name>[Characteristic name]</name>\n      <description>[Brief description with example]</description>\n    </characteristic>\n    <!-- 3-5 characteristics -->\n  </key_characteristics>\n\n  <voice_attributes>\n    <tone>[Primary tone descriptor - secondary descriptor]</tone>\n    <diction>\n      <use>[Comma-separated words and phrases TO USE]</use>\n      <avoid>[Comma-separated words and phrases TO AVOID]</avoid>\n    </diction>\n    <sentence_style>[Description of sentence structure patterns]</sentence_style>\n    <narrative_stance>[Person, voice, storytelling pattern]</narrative_stance>\n  </voice_attributes>\n\n  <language_patterns>\n    <pattern>[Specific pattern with example from samples]</pattern>\n    <!-- 3-5 patterns -->\n  </language_patterns>\n\n  <emotional_expression>\n    <primary_emotions>[Comma-separated emotions]</primary_emotions>\n    <intensity>[low|medium|high]</intensity>\n    <methods>[How emotions are expressed linguistically]</methods>\n  </emotional_expression>\n\n  <dos_and_donts>\n    <do>[Action] - [Brief justification]</do>\n    <!-- 3-5 dos -->\n    <dont>[Avoidance] - [Brief justification]</dont>\n    <!-- 3-5 don'ts -->\n  </dos_and_donts>\n\n  <contextual_variations>\n    <context type=\"formal\">\n      [How voice adapts for formal contexts]\n    </context>\n    <context type=\"casual\">\n      [How voice adapts for casual contexts]\n    </context>\n  </contextual_variations>\n\n  <!-- Only for combined type -->\n  <constraint_hierarchy type=\"combined\">\n    <rule>Company constraints are HARD boundaries - never violate</rule>\n    <rule>Personal patterns apply WITHIN company boundaries</rule>\n    <conflicts>\n      <conflict>\n        <personal>[Pattern that conflicts]</personal>\n        <company>[Constraint it violates]</company>\n        <resolution>[How to handle]</resolution>\n      </conflict>\n    </conflicts>\n  </constraint_hierarchy>\n\n  <purpose>\n    [1-2 sentence primary goal of this voice in AI communications]\n  </purpose>\n\n</BrandVoice>\n</output_schema>\n\n<formatting_rules>\n## Critical Formatting Rules\n\n1. **Word Limit**: Maximum 1000 words. Count before finalizing.\n\n2. **Specificity Over Generality**:\n   - BAD: \"Use a friendly tone\"\n   - GOOD: \"Use contractions, first-person plural ('we'), and end sentences with enthusiasm\"\n\n3. **Examples Are Required**: Every pattern should include at least one example phrase or word.\n\n4. **Quantify When Possible**:\n   - BAD: \"Mix sentence lengths\"\n   - GOOD: \"Alternate between short (5-8 words) and medium (12-18 words) sentences\"\n\n5. **Golden Threads Must Be Concrete**:\n   - BAD: \"Cares about customers\"\n   - GOOD: \"Financial security for the family - reference savings, investment, long-term value\"\n\n6. **Dos/Don'ts Must Be Actionable**:\n   - BAD: \"Don't be boring\"\n   - GOOD: \"Don't use passive voice - always use active constructions\"\n\n7. **No Filler**: Every word must add value. Cut phrases like:\n   - \"It's important to note that...\"\n   - \"When writing, remember to...\"\n   - \"The voice is characterized by...\"\n</formatting_rules>\n\n<instructions>\n## Formatting Process\n\n1. **Parse Input JSON**: Extract all relevant fields from the analyzer output.\n\n2. **Distill to Essentials**: The analyzer provides comprehensive data. Your job is to compress to the most impactful elements.\n\n3. **Prioritize by Impact**:\n   - Golden threads (used for narrative weaving)\n   - Dos/Don'ts (direct AI instructions)\n   - Voice attributes (tone, diction)\n   - Language patterns (specific examples)\n\n4. **Apply Schema**: Format into the exact XML structure required.\n\n5. **Word Count Check**: Count words. If over 1000, cut:\n   - Redundant examples\n   - Secondary characteristics\n   - Verbose descriptions\n\n6. **Validate Structure**: Ensure all required tags are present and properly nested.\n\n7. **Output XML Only**: Return only the XML block. No introduction, no explanation.\n</instructions>\n\n<quality_check>\nBefore outputting, verify:\n- [ ] Word count \u22641000\n- [ ] All required XML tags present\n- [ ] At least 3 golden threads\n- [ ] At least 3 dos AND 3 don'ts\n- [ ] Specific examples included\n- [ ] No generic filler phrases\n- [ ] Proper XML nesting and closure\n</quality_check>",
              "type": "string"
            },
            {
              "id": "validator-prompt",
              "name": "validator_prompt",
              "value": "See assets/prompt-library/agents/echo-validator.md for full prompt",
              "type": "string"
            },
            {
              "id": "min-words",
              "name": "min_word_count",
              "value": 500,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "b0bd702a-2bb5-453f-b756-971f076af931",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -880,
        1056
      ],
      "notes": "CONFIGURE HERE:\n\n1. Replace REPLACE_WITH_YOUR_ANTHROPIC_API_KEY with your actual API key from console.anthropic.com\n2. Copy prompts from assets/prompt-library/agents/echo-*.md\n3. Adjust min_word_count if needed"
    },
    {
      "parameters": {
        "jsCode": "// Validate input fields\n// Handle both Manual Trigger (form) and Normalize External Input (Execute Workflow) paths\nlet input;\ntry {\n  input = $('Manual Trigger').first().json;\n} catch (e) {\n  // Called via Execute Workflow - use Normalize External Input\n  input = $('Normalize External Input').first().json;\n}\n\nconst minWords = $('Configuration').first().json.min_word_count || 500;\nconst errors = [];\nconst config = $('Configuration').first().json;\n\nconst voiceType = input['Voice Type'] || '';\nconst personName = input['Person Name'] || '';\nconst companyName = input['Company Name'] || '';\nconst writingSamples = input['Writing Samples'] || '';\nconst companyMaterials = input['Company Materials'] || '';\nconst skipQC = input['Skip QC Validation'] === 'Yes (Save ~$0.03)';\n\n// Check voice type\nif (!['personal', 'company', 'combined'].includes(voiceType)) {\n  errors.push({\n    field: 'Voice Type',\n    message: 'Must select personal, company, or combined'\n  });\n}\n\n// Check required names based on voice type\nif (voiceType === 'personal' && !personName.trim()) {\n  errors.push({\n    field: 'Person Name',\n    message: 'Person Name required for personal voice type'\n  });\n}\n\nif (voiceType === 'company' && !companyName.trim()) {\n  errors.push({\n    field: 'Company Name',\n    message: 'Company Name required for company voice type'\n  });\n}\n\nif (voiceType === 'combined') {\n  if (!personName.trim()) {\n    errors.push({\n      field: 'Person Name',\n      message: 'Person Name required for combined voice type'\n    });\n  }\n  if (!companyName.trim()) {\n    errors.push({\n      field: 'Company Name',\n      message: 'Company Name required for combined voice type'\n    });\n  }\n}\n\n// Check writing samples\nif (!writingSamples.trim()) {\n  errors.push({\n    field: 'Writing Samples',\n    message: 'Writing samples are required'\n  });\n} else {\n  const wordCount = writingSamples.split(/\\s+/).length;\n  if (wordCount < minWords) {\n    errors.push({\n      field: 'Writing Samples',\n      message: `Minimum ${minWords} words required. Found ${wordCount} words.`\n    });\n  }\n}\n\n// Check company materials for company/combined\nif (['company', 'combined'].includes(voiceType) && !companyMaterials.trim()) {\n  // Warning, not error\n  console.log('Warning: Company materials recommended for company/combined voice type');\n}\n\n// Preserve student_email from Normalize External Input\nconst studentEmail = input.student_email || 'your-email@example.com';\n\n// Return result\nif (errors.length > 0) {\n  return [{\n    json: {\n      validation_passed: false,\n      errors: errors,\n      message: 'Input validation failed',\n      student_email: studentEmail\n    }\n  }];\n}\n\n// Validation passed - normalize inputs\nreturn [{\n  json: {\n    validation_passed: true,\n    voice_type: voiceType,\n    student_name: personName.trim(),\n    company_name: companyName.trim(),\n    writing_samples: writingSamples.trim(),\n    company_materials: companyMaterials.trim(),\n    skip_qc: skipQC,\n    word_count: writingSamples.split(/\\s+/).length,\n    claude_api_key: config.claude_api_key,\n    analyzer_prompt: config.analyzer_prompt,\n    formatter_prompt: config.formatter_prompt,\n    validator_prompt: config.validator_prompt,\n    min_word_count: config.min_word_count,\n    retry_count: 0,\n    student_email: studentEmail\n  }\n}];"
      },
      "id": "75e6dc14-b0a2-4cb1-ae1c-8e16c9a8c84e",
      "name": "Validate Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -656,
        1056
      ],
      "notes": "Validates:\n- Voice type selection\n- Required names for each type\n- Minimum word count in samples\n- Company materials for company/combined"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "validation-check",
              "leftValue": "={{ $json.validation_passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "f95cb263-ff27-4165-8904-df086b2bc4f7",
      "name": "Validation OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -432,
        1056
      ],
      "notes": "Routes based on validation:\n- TRUE: Continue to Option Router\n- FALSE: Return error"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "personal-check",
                    "leftValue": "={{ $json.voice_type }}",
                    "rightValue": "personal",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "personal"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "company-check",
                    "leftValue": "={{ $json.voice_type }}",
                    "rightValue": "company",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "company"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "combined-check",
                    "leftValue": "={{ $json.voice_type }}",
                    "rightValue": "combined",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "combined"
            }
          ]
        },
        "options": {}
      },
      "id": "3b8d6037-df09-4e4c-98b6-247fdd15e1a9",
      "name": "Option Router",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        -208,
        928
      ],
      "notes": "Routes to appropriate analysis path:\n- personal: Individual voice analysis\n- company: Brand voice analysis\n- combined: Hybrid analysis"
    },
    {
      "parameters": {
        "jsCode": "// Build personal voice analysis prompt\nconst input = $input.first().json;\nconst analyzerPrompt = $('Configuration').first().json.analyzer_prompt;\n\n// Personal-specific context\nconst context = `## Personal Voice Analysis Mode\n\nYou are analyzing writing samples to extract **${input.student_name}'s personal voice**.\n\n**Goal**: Create a voice profile that makes AI write exactly like ${input.student_name} - capturing their unique personality, quirks, and communication style.\n\n**Input Focus**:\n- Personal emails, messages, transcripts\n- Social media posts\n- Any authentic communication in their natural voice\n\n**Output Focus**:\n- Individual linguistic patterns\n- Personal vocabulary and phrases\n- Emotional expression style\n- Personality traits through language`;\n\nreturn [{\n  json: {\n    ...input,\n    analysis_context: context,\n    system_prompt: analyzerPrompt\n  }\n}];"
      },
      "id": "b5bf4092-3351-4eaf-b8eb-33c26861aeb8",
      "name": "Build Personal Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        752
      ],
      "notes": "Builds personal voice analysis context"
    },
    {
      "parameters": {
        "jsCode": "// Build company voice analysis prompt\nconst input = $input.first().json;\nconst analyzerPrompt = $('Configuration').first().json.analyzer_prompt;\n\n// Company-specific context\nconst context = `## Company Voice Analysis Mode\n\nYou are analyzing materials to extract the **${input.company_name} brand voice**.\n\n**Goal**: Create a voice profile usable by any employee writing as this company - consistent, professional, and aligned with brand values.\n\n**Input Focus**:\n- Brand style guides\n- Marketing copy and website content\n- Approved customer communications\n- Social media guidelines\n\n**Output Focus**:\n- Company tone and personality\n- Brand-specific vocabulary\n- Communication guidelines\n- Consistency requirements across channels`;\n\nreturn [{\n  json: {\n    ...input,\n    analysis_context: context,\n    system_prompt: analyzerPrompt\n  }\n}];"
      },
      "id": "470f9081-10de-4660-99db-fe32a97ec8a2",
      "name": "Build Company Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        944
      ],
      "notes": "Builds company voice analysis context"
    },
    {
      "parameters": {
        "jsCode": "// Build combined voice analysis prompt\nconst input = $input.first().json;\nconst analyzerPrompt = $('Configuration').first().json.analyzer_prompt;\n\n// Combined-specific context\nconst context = `## Combined Voice Analysis Mode\n\nYou are analyzing samples to extract **${input.student_name}'s voice when representing ${input.company_name}**.\n\n**Goal**: Create a hybrid profile - ${input.student_name}'s authentic personality filtered through ${input.company_name}'s brand constraints.\n\n**Input Focus**:\n- ${input.student_name}'s personal writing samples\n- ${input.company_name}'s brand guidelines/materials\n- (Optional) Past communications as company representative\n\n**Output Focus**:\n- Personal patterns that complement company voice\n- Where personal style enhances brand communication\n- Conflict resolution between personal and company preferences\n- Contextual switching guidance (internal vs external)\n\n**Constraint Hierarchy**:\n1. Company constraints are HARD boundaries (never violate)\n2. Personal patterns applied WITHIN those boundaries\n3. When conflict exists: Company wins, but FLAG the conflict`;\n\nreturn [{\n  json: {\n    ...input,\n    analysis_context: context,\n    system_prompt: analyzerPrompt\n  }\n}];"
      },
      "id": "6ee1465d-6306-483f-8a2c-b0785d464b21",
      "name": "Build Combined Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        1136
      ],
      "notes": "Builds combined voice analysis context with constraint hierarchy"
    },
    {
      "parameters": {
        "jsCode": "// Merge paths back together\nconst items = $input.all();\nif (items.length === 0) {\n  throw new Error('No input received');\n}\nreturn items;"
      },
      "id": "578fccde-ef86-4a07-aea2-cf1da962f8e0",
      "name": "Merge Paths",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        944
      ],
      "notes": "Merges the three voice type paths back to single flow"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-sonnet-4-5-20250929\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.3,\n  \"system\": {{ JSON.stringify($('Configuration').first().json.formatter_prompt) }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify('Generate an XML brand voice snippet from the following analysis data. Output ONLY the XML starting with <BrandVoice> and ending with </BrandVoice>.\\n\\nAnalysis Data:\\n' + JSON.stringify($json, null, 2)) }}\n    }\n  ]\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "a42e6d7a-7e4d-497e-963d-59313de12d6b",
      "name": "Phase 3: XML Generation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        912,
        848
      ],
      "notes": "Phase 3: Generate XML brand voice snippet.\n\nModel: Claude Sonnet 4.5\nTemp: 0.3 (low for consistent formatting)\nMax Tokens: 2000"
    },
    {
      "parameters": {
        "jsCode": "// Extract XML from formatter response\nconst input = $('Merge Analyses').first().json;\nconst httpResponse = $input.first().json;\nconst response = httpResponse.body || httpResponse;\n\n// Check for API errors\nif (response.error) {\n  return [{\n    json: {\n      ...input,\n      phase3_success: false,\n      phase3_error: response.error.message || 'Claude API error'\n    }\n  }];\n}\n\n// Extract text from Claude response\nconst content = response.content?.[0]?.text || '';\n\n// Look for XML in response\nconst xmlMatch = content.match(/<BrandVoice[\\s\\S]*<\\/BrandVoice>/);\nconst xmlOutput = xmlMatch ? xmlMatch[0] : content;\n\n// Count words in XML\nconst wordCount = xmlOutput.split(/\\s+/).length;\n\nreturn [{\n  json: {\n    ...input,\n    phase3_success: !!xmlMatch,\n    brand_voice_xml: xmlOutput,\n    xml_word_count: wordCount,\n    phase3_error: xmlMatch ? null : 'Could not extract XML from response'\n  }\n}];"
      },
      "id": "73dedb60-6887-44e4-abba-0c9516762618",
      "name": "Parse XML",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1136,
        848
      ],
      "notes": "Extracts XML brand voice snippet from formatter response"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "always-skip-qc",
              "leftValue": "={{ true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "abc6f163-9676-4aba-8b15-541c97864f8d",
      "name": "Skip QC?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1360,
        848
      ],
      "notes": "TEMPORARY: Always skip QC for demo.\n\nOriginal condition was: $json.skip_qc === true\n\nTo restore: Change leftValue back to \"={{ $json.skip_qc }}\""
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-sonnet-4-5-20250929\",\n  \"max_tokens\": 2000,\n  \"temperature\": 0,\n  \"system\": {{ JSON.stringify($('Configuration').first().json.validator_prompt) }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify('Validate the following brand voice profile. Output your validation report as JSON.\\n\\n' + $json.brand_voice_xml) }}\n    }\n  ]\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "d8066cfd-c5c0-4a99-8ee6-2b9d78d6a07f",
      "name": "Phase 4: QC Validation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1584,
        912
      ],
      "notes": "Phase 4: Quality validation with emulation test.\n\nModel: Claude Sonnet 4.5\nTemp: 0.0 (CRITICAL - deterministic)\nMax Tokens: 2000"
    },
    {
      "parameters": {
        "jsCode": "// Parse validation response and determine pass/fail\nconst input = $('Parse XML').first().json;\nconst httpResponse = $input.first().json;\nconst response = httpResponse.body || httpResponse;\n\n// Check for API errors\nif (response.error) {\n  return [{\n    json: {\n      ...input,\n      validation_result: 'ERROR',\n      validation_error: response.error.message\n    }\n  }];\n}\n\n// Extract validation JSON\nconst content = response.content?.[0]?.text || '';\nlet validationJson = null;\n\ntry {\n  const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    validationJson = JSON.parse(jsonMatch[0]);\n  }\n} catch (e) {\n  console.log('Validation JSON parse error:', e.message);\n}\n\nconst result = validationJson?.validation_result || 'UNKNOWN';\nconst corrections = validationJson?.corrections_needed || [];\n\nreturn [{\n  json: {\n    ...input,\n    validation_result: result,\n    validation_details: validationJson,\n    corrections_needed: corrections,\n    qc_passed: result === 'PASS'\n  }\n}];"
      },
      "id": "215f4b87-a07f-43d6-9a87-618b58e60448",
      "name": "Parse Validation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1808,
        912
      ],
      "notes": "Parses QC validation result and determines pass/fail"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "qc-passed",
              "leftValue": "={{ $json.qc_passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "id": "01971b4b-203a-448e-8b5e-79a0b877ca38",
      "name": "QC Passed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2032,
        912
      ],
      "notes": "Routes based on QC result:\n- PASS: Continue to output\n- FAIL: Check retry count"
    },
    {
      "parameters": {
        "jsCode": "// Check if we can retry\nconst input = $input.first().json;\nconst retryCount = input.retry_count || 0;\n\nif (retryCount >= 1) {\n  // Max retries reached - output with warning\n  return [{\n    json: {\n      ...input,\n      can_retry: false,\n      output_status: 'WARN_QC_FAILED',\n      message: 'QC validation failed after retry. Output may need manual review.'\n    }\n  }];\n}\n\n// Can retry - increment counter\nreturn [{\n  json: {\n    ...input,\n    retry_count: retryCount + 1,\n    can_retry: true\n  }\n}];"
      },
      "id": "5c9b1c1a-0714-4ece-afa8-0528b5f251f2",
      "name": "Check Retry",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2256,
        992
      ],
      "notes": "Checks if retry is available (max 1 retry).\n\nIf retry available, route back to Phase 3.\nIf max retries, continue with warning."
    },
    {
      "parameters": {
        "jsCode": "// Format final output\nconst input = $input.first().json;\n\n// Determine status\nlet status = 'SUCCESS';\nlet message = 'Brand voice profile generated successfully';\n\nif (input.output_status === 'WARN_QC_FAILED') {\n  status = 'WARN';\n  message = 'Profile generated but QC validation failed. Review recommended.';\n} else if (!input.phase3_success) {\n  status = 'ERROR';\n  message = 'Failed to generate XML output';\n} else if (!input.phase1_success) {\n  status = 'ERROR';\n  message = 'Failed to complete analysis';\n}\n\nreturn [{\n  json: {\n    status: status,\n    message: message,\n    voice_type: input.voice_type,\n    subject: input.student_name || input.company_name,\n    company: input.company_name || null,\n    brand_voice_xml: input.brand_voice_xml,\n    xml_word_count: input.xml_word_count,\n    qc_result: input.validation_result || 'SKIPPED',\n    qc_details: input.validation_details || null,\n    analysis_summary: input.analysis_json?.analysis_summary || null,\n    generated_at: new Date().toISOString(),\n    usage_instructions: {\n      ygm_integration: 'Inject <BrandVoice> block into YGM system prompt under <PersonaAndVoiceDeepDive> section',\n      golden_threads: 'Extract <golden_threads> for Content Finalizer technique',\n      next_step: 'Test voice by drafting sample content'\n    }\n  }\n}];"
      },
      "id": "0f3f3cd8-7cdb-43d5-8265-6bdd81fb2dd8",
      "name": "Format Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2480,
        848
      ],
      "notes": "Formats final output with:\n- Brand voice XML\n- QC result\n- Usage instructions\n- Timestamps"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "error-status",
              "name": "status",
              "value": "ERROR",
              "type": "string"
            },
            {
              "id": "error-message",
              "name": "message",
              "value": "={{ $('Validate Input').first().json.message }}",
              "type": "string"
            },
            {
              "id": "errors",
              "name": "errors",
              "value": "={{ $('Validate Input').first().json.errors }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      },
      "id": "a4e12c09-08b4-40e6-a1a2-a77a1f8c2b8f",
      "name": "Error Output",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -208,
        1152
      ],
      "notes": "Returns validation errors to user"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze SENTENCE STRUCTURE.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY sentence structure - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the SENTENCE STRUCTURE in these writing samples.\\n\\nFocus on:\\n- Average sentence length (count words)\\n- Sentence length variation (short vs medium vs long)\\n- Distribution (% short 1-10 words, % medium 11-25, % long 26+)\\n- Structural patterns (simple, compound, complex)\\n- Rhythm and flow\\n\\nProvide quantitative metrics and specific examples.\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "06acfec8-3491-40c8-94de-86187eb6a30b",
      "name": "Step 1: Sentence Structure",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        -304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze VOCABULARY and WORD CHOICE.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY vocabulary and word choice - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the VOCABULARY and WORD CHOICE in these samples.\\n\\nFocus on:\\n- Vocabulary level (casual, professional, technical, mixed)\\n- Unique word percentage (lexical diversity)\\n- Technical terms frequency\\n- Colloquialisms frequency\\n- Favorite/signature words (with counts)\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "fe4a6912-6cce-4889-9348-b5337c6577b3",
      "name": "Step 2: Vocabulary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        -112
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze TONE and MOOD.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY tone and mood - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the TONE and MOOD in these samples.\\n\\nFocus on:\\n- Primary tone characteristics (enthusiastic, formal, supportive, etc.)\\n- Emotional undercurrent\\n- Effect on reader (how it makes them feel)\\n- Mood progression (opening, middle, closing patterns)\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "1008a117-9df5-486b-a5f7-1c077c5497ad",
      "name": "Step 3: Tone & Mood",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        80
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze SYNTAX and GRAMMAR patterns.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY syntax and grammar - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the SYNTAX and GRAMMAR patterns in these samples.\\n\\nFocus on:\\n- Punctuation habits (semicolons, dashes, exclamation points)\\n- Grammar preferences (formal vs relaxed)\\n- Contraction usage\\n- Sentence starters and transitions\\n- Active vs passive voice\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "98f66a5f-dfdb-4534-af30-af31cd0eccff",
      "name": "Step 4: Syntax & Grammar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        272
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze SEMANTIC PATTERNS.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY semantic patterns - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the SEMANTIC PATTERNS in these samples.\\n\\nFocus on:\\n- Connotative vs denotative language\\n- Abstract vs concrete concepts\\n- Metaphorical frameworks used\\n- Emphasis techniques\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "d00a62fd-7636-4823-a9f0-b632c1ccbcfa",
      "name": "Step 5: Semantic Patterns",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        464
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze PRAGMATIC ELEMENTS.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY pragmatic elements - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the PRAGMATIC ELEMENTS in these samples.\\n\\nFocus on:\\n- Direct vs indirect address (\\\"you\\\" vs \\\"one\\\")\\n- Inclusive language (\\\"we\\\", \\\"us\\\", \\\"together\\\", \\\"y'all\\\")\\n- Persuasion techniques\\n- Engagement strategies (questions, calls to action)\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "7df974bd-a6b4-4326-a30e-5895b03570ee",
      "name": "Step 6: Pragmatic Elements",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        656
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze STYLISTIC DEVICES.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY stylistic devices - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the STYLISTIC DEVICES in these samples.\\n\\nFocus on:\\n- Metaphors and analogies\\n- Alliteration and repetition\\n- Rhetorical questions\\n- Humor usage\\n- Parenthetical asides\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "eba0848d-1e3d-4f01-9964-a4428adc4a26",
      "name": "Step 7: Stylistic Devices",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        848
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze NARRATIVE STANCE.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY narrative stance - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the NARRATIVE STANCE in these samples.\\n\\nFocus on:\\n- First/second/third person preference\\n- Active vs passive voice ratio\\n- Storytelling patterns\\n- Point of view consistency\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "1109491e-5dd3-45d7-a697-08aa9a1a9591",
      "name": "Step 8: Narrative Stance",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1040
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze THEMES and VALUES.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY themes and values - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the THEMES and VALUES in these samples.\\n\\nFocus on:\\n- Recurring themes across samples\\n- Implied values and beliefs\\n- What they emphasize vs de-emphasize\\n- Core motivations\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "4820cc1a-fa82-431d-a71a-719f4d8228ab",
      "name": "Step 9: Themes & Values",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1232
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze EMOTIONAL EXPRESSION.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY emotional expression - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the EMOTIONAL EXPRESSION in these samples.\\n\\nFocus on:\\n- Primary emotions conveyed\\n- Emotional range (narrow vs broad)\\n- Methods of emotional expression (word choice, punctuation, structure)\\n- Intensity level\\n- Variation by context\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "f982ca10-6b65-49aa-aed1-9c0d994bf931",
      "name": "Step 10: Emotional Expression",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1424
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze WRITER PERSONA.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY writer persona - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Based on all the writing samples, synthesize a WRITER PERSONA.\\n\\nFocus on:\\n- How would you describe this person in 2-3 sentences?\\n- What's their relationship to the audience?\\n- What role do they play (teacher, friend, expert, guide)?\\n- What makes them distinctive?\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "24c561a0-0d64-440e-86db-311ddd24ca9f",
      "name": "Step 11: Writer Persona",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1616
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze VOCABULARY LIST.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY vocabulary compilation - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Compile a comprehensive VOCABULARY LIST from these samples.\\n\\nCreate 5 categories:\\n1. Common Words (frequently used, define the style)\\n2. Signature Phrases (distinctive expressions used repeatedly)\\n3. Industry/Domain Terms (specialized vocabulary)\\n4. Transitional Phrases (how they connect ideas)\\n5. Emotional/Motivational Language (words that convey feeling or action)\\n\\nProvide word counts where possible.\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "85ed52ac-c79a-463e-a118-9745ee98e7aa",
      "name": "Step 12: Vocabulary Compilation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1808
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze CONTEXTUAL VARIATIONS.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY contextual variations - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze how the voice VARIES BY CONTEXT in these samples.\\n\\nIdentify:\\n- Formal vs casual adaptations\\n- Live vs prepared content differences\\n- What stays consistent across contexts\\n- What changes and when\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "e4676ec2-1ca9-461a-bf17-51db8117b8c5",
      "name": "Step 13: Contextual Variations",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        2000
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze DOS and DON'TS.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY dos and don'ts - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Based on all the writing samples, generate comprehensive DOS and DON'TS.\\n\\nRequirements:\\n- Minimum 5 of each\\n- Be specific and actionable\\n- Include justification for each\\n- Examples where helpful\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "5d1570f5-31e8-4527-9ff0-cd0ff2a17a07",
      "name": "Step 14: Dos & Don'ts",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        2192
      ]
    },
    {
      "parameters": {
        "jsCode": "// Echo Multi-Step: Merge All Analyses (v4 - Sequential Direct References)\n// Each step runs sequentially, so we can reference them directly by name\n\nconst MAX_TEXT_LENGTH = 50000;\nconst inputData = $('Merge Paths').first().json;\n\nconst stepNodes = [\n  { key: 'sentence_structure', node: 'Step 1: Sentence Structure' },\n  { key: 'vocabulary', node: 'Step 2: Vocabulary' },\n  { key: 'tone_mood', node: 'Step 3: Tone & Mood' },\n  { key: 'syntax_grammar', node: 'Step 4: Syntax & Grammar' },\n  { key: 'semantic_patterns', node: 'Step 5: Semantic Patterns' },\n  { key: 'pragmatic_elements', node: 'Step 6: Pragmatic Elements' },\n  { key: 'stylistic_devices', node: 'Step 7: Stylistic Devices' },\n  { key: 'narrative_stance', node: 'Step 8: Narrative Stance' },\n  { key: 'themes_values', node: 'Step 9: Themes & Values' },\n  { key: 'emotional_expression', node: 'Step 10: Emotional Expression' },\n  { key: 'writer_persona', node: 'Step 11: Writer Persona' },\n  { key: 'vocabulary_compilation', node: 'Step 12: Vocabulary Compilation' },\n  { key: 'contextual_variations', node: 'Step 13: Contextual Variations' },\n  { key: 'dos_donts', node: \"Step 14: Dos & Don'ts\" }\n];\n\nconst fullAnalysis = {\n  voice_type: inputData.voice_type,\n  subject: { name: inputData.student_name, company: inputData.company_name || null },\n  skip_qc: inputData.skip_qc,\n  timestamp: new Date().toISOString(),\n  analyses: {},\n  golden_threads: [],\n  processing_stats: { steps_succeeded: 0, steps_failed: [], processing_time_ms: 0 }\n};\n\nconst startTime = Date.now();\nlet successCount = 0;\nconst failedSteps = [];\n\n// Process each step by direct node reference\nfor (const step of stepNodes) {\n  try {\n    const response = $(step.node).first().json;\n    let stepData = null;\n\n    // Parse Claude API response\n    if (response.content?.[0]?.text) {\n      const text = (response.content[0].text || '').substring(0, MAX_TEXT_LENGTH);\n      const trimmed = text.trim();\n      \n      // Try JSON.parse first\n      if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n        try { stepData = JSON.parse(trimmed); } catch (e) { /* continue to regex */ }\n      }\n      \n      // Try markdown code block extraction\n      if (!stepData) {\n        const jsonMatch = text.match(/```(?:json)?\\n?([\\s\\S]{1,48000}?)\\n?```/);\n        if (jsonMatch?.[1]) {\n          try { stepData = JSON.parse(jsonMatch[1]); }\n          catch (e) { stepData = { raw_analysis: jsonMatch[1].substring(0, 10000) }; }\n        } else {\n          stepData = { raw_analysis: text.substring(0, 10000) };\n        }\n      }\n    } else if (typeof response === 'object') {\n      stepData = response;\n    }\n\n    fullAnalysis.analyses[step.key] = stepData || { error: 'Parse failed' };\n    successCount++;\n  } catch (error) {\n    failedSteps.push({ step: step.key, node: step.node, error: error.message });\n    fullAnalysis.analyses[step.key] = { error: error.message };\n  }\n}\n\n// Return results with stats\nfullAnalysis.processing_stats = {\n  steps_succeeded: successCount,\n  steps_failed: failedSteps,\n  partial_result: failedSteps.length > 0,\n  processing_time_ms: Date.now() - startTime,\n  total_steps: stepNodes.length\n};\n\nfullAnalysis.golden_threads = extractGoldenThreads(fullAnalysis);\n\nreturn [{ json: fullAnalysis }];\n\nfunction extractGoldenThreads(analysis) {\n  const threads = [];\n  try {\n    const themes = analysis.analyses.themes_values?.analysis?.primary_themes ||\n                   analysis.analyses.themes_values?.primary_themes;\n    if (themes && Array.isArray(themes)) {\n      themes.slice(0, 3).forEach((t, i) => {\n        threads.push({ priority: i+1, thread: typeof t === 'string' ? t : t.theme || t.name, source: 'themes_values' });\n      });\n    }\n    if (threads.length < 3) {\n      const persona = analysis.analyses.writer_persona?.distinctive_quality ||\n                      analysis.analyses.writer_persona?.analysis?.distinctive_quality;\n      if (persona) {\n        threads.push({ priority: threads.length+1, thread: persona, source: 'writer_persona' });\n      }\n    }\n  } catch (e) { console.log('Golden threads extraction error:', e.message); }\n  return threads;\n}"
      },
      "id": "730efbca-9d86-411c-bfee-e1526f57138c",
      "name": "Merge Analyses",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        688,
        848
      ]
    },
    {
      "parameters": {
        "jsCode": "// Create Student-Friendly Output\n// Formats the brand voice output for easy student consumption\n\nconst input = $input.first().json;\n\n// Generate timestamp for files\nconst timestamp = new Date().toISOString().split('T')[0];\n// Handle subject being either string or object with .name property\nconst subjectName = (typeof input.subject === 'object' ? input.subject?.name : input.subject) || input.student_name || input.person_name || 'Unknown';\n\n// Create XML snippet file content\nconst xmlSnippet = input.brand_voice_xml || '<BrandVoice>No XML generated</BrandVoice>';\n\n// Create Quick Reference markdown\nconst quickReference = `# ${subjectName} - Brand Voice Quick Reference\n\n## Golden Threads\nThese are the core themes that should permeate all your communications:\n\n${input.analysis_summary?.golden_threads?.map((t, i) => `${i + 1}. **${t.thread}** - ${t.evidence}`).join('\\n') || '- Golden threads will be extracted from full analysis'}\n\n## Do's\n- Use your signature phrases and expressions\n- Maintain your natural tone and energy\n- Let your personality shine through\n\n## Don'ts  \n- Don't over-formalize your language\n- Don't lose your authentic voice\n- Don't ignore your natural communication style\n\n## Usage Instructions\n\n### For AI Assistants (Sugar/YGM)\n1. Copy the XML snippet below\n2. Paste into your agent's system prompt\n3. Place under \\`<PersonaAndVoiceDeepDive>\\` section\n\n### For RAG Enhancement\n1. Save the Full Analysis as a .md file\n2. Upload to Gemini File Store\n3. Reference in your agent's knowledge base\n\n---\nGenerated: ${timestamp}\nVoice Type: ${input.voice_type}\n`;\n\n// Format all 14 analysis dimensions into markdown sections\nconst analysesSection = Object.entries($('Merge Analyses').first().json.analyses || {}).map(([key, data]) => {\n  const title = key.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');\n  const content = typeof data === 'string' ? data : (data.raw_analysis || data.analysis || JSON.stringify(data, null, 2));\n  return `### ${title}\\n\\n${content}`;\n}).join('\\n\\n---\\n\\n');\n\n// Create Full Analysis markdown with ALL 14 dimensions\nconst fullAnalysis = `# ${subjectName} - Complete Brand Voice Analysis\n\n## Overview\n- **Voice Type**: ${input.voice_type}\n- **Subject**: ${subjectName}\n- **Company**: ${input.company || 'N/A'}\n- **Generated**: ${timestamp}\n- **QC Result**: ${input.qc_result || 'SKIPPED'}\n\n## Brand Voice XML Snippet\n\\`\\`\\`xml\n${xmlSnippet}\n\\`\\`\\`\n\n## Detailed Analyses (14 Dimensions)\n\n${analysesSection}\n\n## Analysis Summary\nThis profile was generated from ${Object.keys($('Merge Analyses').first().json.analyses || {}).length} analysis dimensions totaling ${input.xml_word_count || 0} words.\n\n## Next Steps\n\n1. **Integrate with AI Assistant**\n   - Copy the XML snippet above\n   - Paste into your Sugar/YGM agent system prompt\n   - Test with a sample email or social post\n\n2. **Enable RAG Enhancement**\n   - Save this full analysis as \\`${subjectName.toLowerCase().replace(/\\s+/g, '-')}-brand-voice.md\\`\n   - Upload to Gemini File Store via the Librarian Tool\n   - Your agent will now reference your voice profile for context\n\n3. **Validate the Voice**\n   - Ask your agent to write a sample piece\n   - Compare against your actual writing\n   - Refine if needed by adding more writing samples to Echo\n\n---\n*Generated by Echo Brand Voice Analysis*\n`;\n\n// Return enhanced output with student-friendly structure\nreturn [{\n  json: {\n    // Original output fields\n    ...input,\n    \n    // Student-friendly additions\n    student_output: {\n      status: 'SUCCESS',\n      message: `Your brand voice profile for ${subjectName} is ready!`,\n      \n      // Ready-to-use files\n      files: {\n        xml_snippet: {\n          filename: `${subjectName.toLowerCase().replace(/\\s+/g, '-')}-brand-voice.xml`,\n          content: xmlSnippet,\n          instructions: 'Copy this XML and paste into your AI agent system prompt under <PersonaAndVoiceDeepDive>'\n        },\n        quick_reference: {\n          filename: `${subjectName.toLowerCase().replace(/\\s+/g, '-')}-quick-reference.md`,\n          content: quickReference,\n          instructions: 'Keep this handy when writing prompts or reviewing AI output'\n        },\n        full_analysis: {\n          filename: `${subjectName.toLowerCase().replace(/\\s+/g, '-')}-full-analysis.md`,\n          content: fullAnalysis,\n          instructions: 'Upload to Gemini File Store for RAG-enhanced responses'\n        }\n      },\n      \n      // Quick access to key outputs\n      brand_voice_xml: xmlSnippet,\n      \n      // Clear next steps\n      next_steps: [\n        {\n          step: 1,\n          action: 'Copy brand_voice_xml into your AI assistant system prompt',\n          detail: 'Place under <PersonaAndVoiceDeepDive> section in Sugar or YGM'\n        },\n        {\n          step: 2,\n          action: 'Upload full_analysis.md to Gemini File Store',\n          detail: 'Use Librarian Tool workflow to add to your knowledge base'\n        },\n        {\n          step: 3,\n          action: 'Test your voice by asking the agent to write a sample email',\n          detail: 'Compare output against your actual writing style'\n        }\n      ]\n    }\n  }\n}];"
      },
      "id": "student-output-node",
      "name": "Create Student Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2704,
        848
      ]
    },
    {
      "parameters": {
        "inputSource": "passthrough"
      },
      "id": "execute-workflow-trigger",
      "name": "Called by Trigger Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        -1328,
        800
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "voice-type",
              "name": "Voice Type",
              "value": "={{ $json.voice_type || 'personal' }}",
              "type": "string"
            },
            {
              "id": "person-name",
              "name": "Person Name",
              "value": "={{ $json.person_name || '' }}",
              "type": "string"
            },
            {
              "id": "company-name",
              "name": "Company Name",
              "value": "={{ $json.company_name || '' }}",
              "type": "string"
            },
            {
              "id": "writing-samples",
              "name": "Writing Samples",
              "value": "={{ $json.writing_samples || '' }}",
              "type": "string"
            },
            {
              "id": "company-materials",
              "name": "Company Materials",
              "value": "={{ $json.company_materials || '' }}",
              "type": "string"
            },
            {
              "id": "skip-qc",
              "name": "Skip QC Validation",
              "value": "={{ $json.skip_qc ? 'Yes (Save ~$0.03)' : 'No (Recommended)' }}",
              "type": "string"
            },
            {
              "id": "student-email",
              "name": "student_email",
              "value": "={{ $json.student_email || 'your-email@example.com' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "normalize-external-input",
      "name": "Normalize External Input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -1104,
        800
      ]
    },
    {
      "parameters": {
        "jsCode": "// Create file data for email attachments\nconst input = $input.first().json;\nconst studentOutput = input.student_output || {};\nconst files = studentOutput.files || {};\n\n// Get the brand voice XML\nconst xmlContent = files.xml_snippet?.content || input.brand_voice_xml || '<BrandVoice>No content</BrandVoice>';\nconst fullAnalysis = files.full_analysis?.content || '# Brand Voice Analysis\\n\\nNo analysis available.';\n\n// Get subject name for file naming - handle subject being object or string\nconst subjectName = (typeof input.subject === 'object' ? input.subject?.name : input.subject) || input.student_name || input.person_name || 'brand';\nconst safeSubjectName = subjectName.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n\n// Get student email from workflow context\nconst studentEmail = $('Normalize External Input').first()?.json?.student_email || \n                     $('Manual Trigger').first()?.json?.email || \n                     'your-email@example.com';\n\nreturn [\n  {\n    json: {\n      fileName: `${safeSubjectName}-brand-voice.xml`,\n      fileContent: xmlContent,\n      mimeType: 'application/xml',\n      student_email: studentEmail,\n      subject_name: subjectName\n    }\n  },\n  {\n    json: {\n      fileName: `${safeSubjectName}-full-analysis.md`,\n      fileContent: fullAnalysis,\n      mimeType: 'text/markdown',\n      student_email: studentEmail,\n      subject_name: subjectName\n    }\n  }\n];"
      },
      "id": "create-files",
      "name": "Create Files for Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2928,
        848
      ]
    },
    {
      "parameters": {
        "mode": "jsonToBinary",
        "convertAllData": false,
        "sourceKey": "fileContent",
        "destinationKey": "data",
        "options": {
          "encoding": "utf8",
          "fileName": "={{ $json.fileName }}",
          "mimeType": "={{ $json.mimeType }}"
        }
      },
      "id": "convert-to-binary",
      "name": "Convert to Binary",
      "type": "n8n-nodes-base.moveBinaryData",
      "typeVersion": 1.1,
      "position": [
        3152,
        848
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "sendTo": "={{ $json.student_email || 'your-email@example.com' }}",
        "subject": "Your Brand Voice Analysis is Ready!",
        "emailType": "text",
        "message": "Hi there!\n\nYour brand voice analysis is complete. You'll find two files attached:\n\n1. brand-voice.xml - Paste this into your AI agent's system prompt under <PersonaAndVoiceDeepDive>\n2. full-analysis.md - Upload this to your knowledge base for reference\n\nNext steps:\n1. Open your AI agent configuration\n2. Find the <PersonaAndVoiceDeepDive> section in your system prompt\n3. Paste the XML content there\n4. Save and test!\n\nQuestions? Reply to this email or ask in the Friday session.\n\n- Tyler & Sara\nAI Build Lab",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "attachment_0"
              },
              {
                "property": "attachment_1"
              }
            ]
          }
        }
      },
      "id": "send-email",
      "name": "Send Results Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        3600,
        848
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notes": "IMPORTANT: Configure your Gmail OAuth2 credentials here after importing"
    },
    {
      "id": "prepare-attachments",
      "name": "Prepare Attachments",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3376,
        848
      ],
      "parameters": {
        "jsCode": "// Prepare Attachments for Gmail\n// Collects binary data from all items into a single item with multiple binary properties\n\nconst items = $input.all();\n\nif (items.length === 0) {\n  throw new Error('No items to process');\n}\n\n// Get email info from first item\nconst firstItem = items[0].json;\nconst studentEmail = firstItem.student_email || 'your-email@example.com';\nconst subjectName = firstItem.subject_name || 'Unknown';\n\n// Build output item with all binary attachments\nconst outputItem = {\n  json: {\n    student_email: studentEmail,\n    subject_name: subjectName,\n    attachment_count: items.length\n  },\n  binary: {}\n};\n\n// Copy binary data from each item, naming them attachment_0, attachment_1, etc.\nitems.forEach((item, index) => {\n  if (item.binary && item.binary.data) {\n    outputItem.binary[`attachment_${index}`] = item.binary.data;\n  }\n});\n\nreturn [outputItem];"
      }
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configuration": {
      "main": [
        [
          {
            "node": "Validate Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Input": {
      "main": [
        [
          {
            "node": "Validation OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validation OK?": {
      "main": [
        [
          {
            "node": "Option Router",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Option Router": {
      "main": [
        [
          {
            "node": "Build Personal Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Company Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Combined Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Personal Prompt": {
      "main": [
        [
          {
            "node": "Merge Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Company Prompt": {
      "main": [
        [
          {
            "node": "Merge Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Combined Prompt": {
      "main": [
        [
          {
            "node": "Merge Paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Paths": {
      "main": [
        [
          {
            "node": "Step 1: Sentence Structure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Phase 3: XML Generation": {
      "main": [
        [
          {
            "node": "Parse XML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse XML": {
      "main": [
        [
          {
            "node": "Skip QC?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip QC?": {
      "main": [
        [
          {
            "node": "Format Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Phase 4: QC Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Phase 4: QC Validation": {
      "main": [
        [
          {
            "node": "Parse Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Validation": {
      "main": [
        [
          {
            "node": "QC Passed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "QC Passed?": {
      "main": [
        [
          {
            "node": "Format Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check Retry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Retry": {
      "main": [
        [
          {
            "node": "Format Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Analyses": {
      "main": [
        [
          {
            "node": "Phase 3: XML Generation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Output": {
      "main": [
        [
          {
            "node": "Create Student Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Called by Trigger Workflow": {
      "main": [
        [
          {
            "node": "Normalize External Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize External Input": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Student Output": {
      "main": [
        [
          {
            "node": "Create Files for Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Files for Email": {
      "main": [
        [
          {
            "node": "Convert to Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 1: Sentence Structure": {
      "main": [
        [
          {
            "node": "Step 2: Vocabulary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 2: Vocabulary": {
      "main": [
        [
          {
            "node": "Step 3: Tone & Mood",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 3: Tone & Mood": {
      "main": [
        [
          {
            "node": "Step 4: Syntax & Grammar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 4: Syntax & Grammar": {
      "main": [
        [
          {
            "node": "Step 5: Semantic Patterns",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 5: Semantic Patterns": {
      "main": [
        [
          {
            "node": "Step 6: Pragmatic Elements",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 6: Pragmatic Elements": {
      "main": [
        [
          {
            "node": "Step 7: Stylistic Devices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 7: Stylistic Devices": {
      "main": [
        [
          {
            "node": "Step 8: Narrative Stance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 8: Narrative Stance": {
      "main": [
        [
          {
            "node": "Step 9: Themes & Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 9: Themes & Values": {
      "main": [
        [
          {
            "node": "Step 10: Emotional Expression",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 10: Emotional Expression": {
      "main": [
        [
          {
            "node": "Step 11: Writer Persona",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 11: Writer Persona": {
      "main": [
        [
          {
            "node": "Step 12: Vocabulary Compilation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 12: Vocabulary Compilation": {
      "main": [
        [
          {
            "node": "Step 13: Contextual Variations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 13: Contextual Variations": {
      "main": [
        [
          {
            "node": "Step 14: Dos & Don'ts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 14: Dos & Don'ts": {
      "main": [
        [
          {
            "node": "Merge Analyses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to Binary": {
      "main": [
        [
          {
            "node": "Prepare Attachments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Attachments": {
      "main": [
        [
          {
            "node": "Send Results Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}