Files
Claude-Code-Workflow/.claude/skills/workflow-skill-designer/phases/02-orchestrator-design.md
catlog22 47c192f584 Add orchestrator design, phase file generation, and validation processes
- Implement Phase 2: Orchestrator Design with detailed steps for generating SKILL.md
- Introduce Phase 3: Phase Files Design to create phase files with content fidelity
- Establish Phase 4: Validation & Integration to ensure structural completeness and reference integrity
- Include comprehensive validation checks for content quality and data flow consistency
- Enhance documentation with clear objectives and critical rules for each phase
2026-02-05 19:29:45 +08:00

13 KiB

Phase 2: Orchestrator Design

Generate the SKILL.md orchestrator file from workflowConfig, applying all coordination patterns (progressive loading, TodoWrite, data flow, conditional execution).

Objective

  • Create .claude/skills/{skillName}/SKILL.md as pure coordinator
  • Apply frontmatter conversion rules
  • Generate architecture diagram from phase structure
  • Build execution flow with Ref: markers and phase reference table
  • Generate data flow diagram
  • Build TodoWrite attachment/collapse patterns from phase definitions
  • Include all orchestrator-level sections

Step 2.1: Create Directory Structure

skillDir=".claude/skills/${workflowConfig.skillName}"
mkdir -p "${skillDir}/phases"

# Optional directories based on features
# mkdir -p "${skillDir}/specs"      # if has domain specifications
# mkdir -p "${skillDir}/templates"  # if has reusable templates

Step 2.2: Generate Frontmatter

function generateFrontmatter(config) {
  return `---
name: ${config.skillName}
description: ${config.description}. Triggers on ${config.triggers.map(t => `"${t}"`).join(', ')}.
allowed-tools: ${config.allowedTools.join(', ')}
---`;
}

Conversion from command frontmatter:

// If source is command_set, convert fields:
function convertCommandFrontmatter(commandFm, config) {
  return {
    name: commandFm.group
      ? `${commandFm.group}-${commandFm.name}`  // "workflow" + "plan" → "workflow-plan"
      : commandFm.name,
    description: commandFm.description,
    // argument-hint → removed (handled in Input Processing section)
    // examples → removed (moved to inline docs)
    // group → embedded in name prefix
    allowedTools: expandToolWildcards(commandFm['allowed-tools'])
    // "Skill(*), TodoWrite(*), Read(*)" → "Task, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep, Skill"
  };
}

// Expand tool wildcards
function expandToolWildcards(toolsStr) {
  const expanded = toolsStr
    .replace(/Skill\(\*\)/g, 'Skill')
    .replace(/TodoWrite\(\*\)/g, 'TodoWrite')
    .replace(/Read\(\*\)/g, 'Read')
    .replace(/Bash\(\*\)/g, 'Bash')
    .replace(/Glob\(\*\)/g, 'Glob')
    .replace(/Grep\(\*\)/g, 'Grep')
    .replace(/Task\(\*\)/g, 'Task');

  // Add commonly needed tools if not present
  const baseTools = ['Task', 'AskUserQuestion', 'TodoWrite', 'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'];
  const current = expanded.split(',').map(t => t.trim());
  const merged = [...new Set([...current, ...baseTools])];
  return merged;
}

Step 2.3: Generate Architecture Diagram

function generateArchitectureDiagram(config) {
  const phases = config.phases;
  const maxWidth = 65;

  let diagram = '```\n';
  diagram += '┌' + '─'.repeat(maxWidth) + '┐\n';
  diagram += `│  ${config.title} Orchestrator (SKILL.md)${' '.repeat(maxWidth - config.title.length - 30)}│\n`;
  diagram += `│  → Pure coordinator: Execute phases, parse outputs, pass context${' '.repeat(maxWidth - 64)}│\n`;
  diagram += '└' + '─'.repeat(Math.floor(maxWidth/2)) + '┬' + '─'.repeat(maxWidth - Math.floor(maxWidth/2) - 1) + '┘\n';

  // Phase boxes
  diagram += '                │\n';
  diagram += '    ' + phases.map(() => '┌─────────┐').join(' ') + '\n';
  diagram += '    ' + phases.map((p, i) => {
    const label = `Phase ${p.number}`.padEnd(9);
    return `│${label}│`;
  }).join(' ') + '\n';
  diagram += '    ' + phases.map(p => {
    const name = p.name.substring(0, 9).padEnd(9);
    return `│${name}│`;
  }).join(' ') + '\n';
  diagram += '    ' + phases.map(() => '└─────────┘').join(' ') + '\n';

  // Output labels
  diagram += '    ' + phases.map(p => {
    const vars = p.outputVariables.join(', ').substring(0, 11).padEnd(11);
    return `  ${vars}`;
  }).join('') + '\n';

  diagram += '```';
  return diagram;
}

Step 2.4: Generate Execution Flow

The execution flow uses Ref: markers to point to phase documents, with a Phase Reference Documents table inline.

function generateExecutionFlow(config) {
  let flow = '## Execution Flow\n\n```\n';
  flow += 'Input Parsing:\n';
  flow += '   └─ Convert user input to structured format (GOAL/SCOPE/CONTEXT)\n\n';

  for (const phase of config.phases) {
    flow += `Phase ${phase.number}: ${phase.name}\n`;

    if (phase.isConditional) {
      flow += `   └─ Decision (${phase.condition}):\n`;
      flow += `      ├─ condition met → Ref: phases/${String(phase.number).padStart(2, '0')}-${phase.slug}.md\n`;
      if (phase.todoWriteSubTasks.length > 0) {
        flow += `      │   ├─ Tasks attached: ${phase.todoWriteSubTasks.join(' → ')}\n`;
      }
      flow += `      │   └─ Output: ${phase.outputFiles.join(', ') || phase.outputVariables.join(', ')}\n`;
      flow += `      └─ condition not met → Skip to Phase ${phase.number + 1}\n`;
    } else {
      flow += `   └─ Ref: phases/${String(phase.number).padStart(2, '0')}-${phase.slug}.md\n`;
      if (phase.todoWriteSubTasks.length > 0) {
        flow += `      ├─ Tasks attached: ${phase.todoWriteSubTasks.join(' → ')}\n`;
      }
      flow += `      └─ Output: ${[...phase.outputVariables, ...phase.outputFiles].join(', ')}\n`;
    }
    flow += '\n';
  }

  flow += 'Return:\n   └─ Summary with recommended next steps\n';
  flow += '```\n\n';

  // Phase Reference Documents table
  flow += '**Phase Reference Documents** (read on-demand when phase executes):\n\n';
  flow += '| Phase | Document | Purpose |\n';
  flow += '|-------|----------|---------|\n';
  for (const phase of config.phases) {
    const filename = `${String(phase.number).padStart(2, '0')}-${phase.slug}.md`;
    flow += `| ${phase.number} | [phases/${filename}](phases/${filename}) | ${phase.description} |\n`;
  }

  return flow;
}

Step 2.5: Generate Data Flow Section

function generateDataFlow(config) {
  let section = '## Data Flow\n\n```\n';
  section += 'User Input (task description)\n';
  section += '    ↓\n';
  section += '[Convert to Structured Format]\n';

  for (const phase of config.phases) {
    const inputVars = config.dataFlow
      .filter(d => d.to === `phase${phase.number}`)
      .flatMap(d => d.variables);
    const outputVars = [...phase.outputVariables, ...phase.outputFiles];

    section += '    ↓\n';
    section += `Phase ${phase.number}: ${phase.name}\n`;
    if (inputVars.length > 0) {
      section += `    ↓ Input: ${inputVars.join(' + ')}\n`;
    }
    if (outputVars.length > 0) {
      section += `    ↓ Output: ${outputVars.join(' + ')}\n`;
    }
    if (phase.isConditional) {
      section += `    ↓ Skip if ${phase.condition} is false → proceed to Phase ${phase.number + 1}\n`;
    }
  }

  section += '    ↓\n';
  section += 'Return summary to user\n';
  section += '```\n';
  return section;
}

Step 2.6: Generate TodoWrite Pattern

function generateTodoWritePattern(config) {
  let section = '## TodoWrite Pattern\n\n';
  section += '**Core Concept**: Dynamic task attachment and collapse for real-time visibility.\n\n';

  section += '### Key Principles\n\n';
  section += '1. **Task Attachment** (when phase executed):\n';
  section += '   - Sub-tasks are **attached** to orchestrator\'s TodoWrite\n';

  // Identify which phases have sub-tasks
  const phasesWithSubTasks = config.phases.filter(p => p.todoWriteSubTasks.length > 0);
  const phasesWithoutSubTasks = config.phases.filter(p => p.todoWriteSubTasks.length === 0);

  if (phasesWithSubTasks.length > 0) {
    section += `   - **${phasesWithSubTasks.map(p => `Phase ${p.number}`).join(', ')}**: Multiple sub-tasks attached\n`;
  }
  if (phasesWithoutSubTasks.length > 0) {
    section += `   - **${phasesWithoutSubTasks.map(p => `Phase ${p.number}`).join(', ')}**: Single task (atomic)\n`;
  }

  section += '\n2. **Task Collapse** (after sub-tasks complete):\n';
  if (phasesWithSubTasks.length > 0) {
    section += `   - **Applies to ${phasesWithSubTasks.map(p => `Phase ${p.number}`).join(', ')}**: Remove sub-tasks, collapse to summary\n`;
  }
  section += '   - Maintains clean orchestrator-level view\n';

  section += '\n3. **Continuous Execution**: After completion, automatically proceed to next phase\n\n';

  // Generate TodoWrite examples for phases with sub-tasks
  for (const phase of phasesWithSubTasks) {
    section += `### Phase ${phase.number} (Tasks Attached):\n`;
    section += '```json\n[\n';

    // Previous phases completed
    for (const prev of config.phases.filter(p => p.number < phase.number)) {
      section += `  {"content": "Phase ${prev.number}: ${prev.name}", "status": "completed"},\n`;
    }

    // Current phase in_progress with sub-tasks
    section += `  {"content": "Phase ${phase.number}: ${phase.name}", "status": "in_progress"},\n`;
    phase.todoWriteSubTasks.forEach((task, i) => {
      const status = i === 0 ? 'in_progress' : 'pending';
      section += `  {"content": "  → ${task}", "status": "${status}"},\n`;
    });

    // Remaining phases pending
    for (const next of config.phases.filter(p => p.number > phase.number && !p.isConditional)) {
      section += `  {"content": "Phase ${next.number}: ${next.name}", "status": "pending"},\n`;
    }

    section += ']\n```\n\n';

    // Collapsed version
    section += `### Phase ${phase.number} (Collapsed):\n`;
    section += '```json\n[\n';
    for (const p of config.phases.filter(pp => !pp.isConditional || pp.number <= phase.number)) {
      const status = p.number <= phase.number ? 'completed' : 'pending';
      section += `  {"content": "Phase ${p.number}: ${p.name}", "status": "${status}"},\n`;
    }
    section += ']\n```\n\n';
  }

  return section;
}

Step 2.7: Generate Remaining Sections

Extract from source orchestrator or generate from config:

function generateOrchestratorSections(config, sourceContent) {
  const sections = [];

  // Auto Mode (if feature enabled)
  if (config.features.hasAutoMode) {
    sections.push(extractOrGenerate(sourceContent, 'Auto Mode',
      '## Auto Mode\n\nWhen `--yes` or `-y`: Auto-continue all phases, use recommended defaults.\n'));
  }

  // Core Rules
  sections.push(extractOrGenerate(sourceContent, 'Core Rules',
    generateDefaultCoreRules(config)));

  // Input Processing
  sections.push(extractOrGenerate(sourceContent, 'Input Processing',
    generateDefaultInputProcessing(config)));

  // Post-Phase Updates (if feature enabled)
  if (config.features.hasPostPhaseUpdates) {
    sections.push(extractOrGenerate(sourceContent, 'Post-Phase Updates',
      generatePostPhaseUpdates(config)));
  }

  // Error Handling
  sections.push(extractOrGenerate(sourceContent, 'Error Handling',
    generateDefaultErrorHandling()));

  // Coordinator Checklist
  sections.push(extractOrGenerate(sourceContent, 'Coordinator Checklist',
    generateCoordinatorChecklist(config)));

  // Related Commands
  sections.push(extractOrGenerate(sourceContent, 'Related Commands',
    generateRelatedCommands(config)));

  return sections.join('\n\n');
}

// Extract section from source if exists, otherwise generate default
function extractOrGenerate(sourceContent, sectionName, defaultContent) {
  if (sourceContent) {
    const extracted = extractSection(sourceContent, sectionName);
    if (extracted) return extracted;
  }
  return defaultContent;
}

// Default Core Rules template
function generateDefaultCoreRules(config) {
  return `## Core Rules

1. **Start Immediately**: First action is TodoWrite initialization, second action is Phase 1 execution
2. **No Preliminary Analysis**: Do not read files or gather context before Phase 1
3. **Parse Every Output**: Extract required data from each phase for next phase
4. **Auto-Continue**: Check TodoList status to execute next pending phase automatically
5. **Track Progress**: Update TodoWrite dynamically with task attachment/collapse pattern
6. **Progressive Phase Loading**: Read phase docs ONLY when that phase is about to execute
7. **DO NOT STOP**: Continuous multi-phase workflow until all phases complete`;
}

// Default Error Handling template
function generateDefaultErrorHandling() {
  return `## Error Handling

- **Parsing Failure**: If output parsing fails, retry once, then report error
- **Validation Failure**: Report which file/data is missing
- **Command Failure**: Keep phase \`in_progress\`, report error, do not proceed`;
}

Step 2.8: Assemble SKILL.md

function assembleSkillMd(config, sourceContent) {
  const parts = [
    generateFrontmatter(config),
    '',
    `# ${config.title}`,
    '',
    config.description,
    '',
    generateArchitectureDiagram(config),
    '',
    generateDesignPrinciples(config),
    '',
    generateExecutionFlow(config),
    '',
    generateDataFlow(config),
    '',
    generateTodoWritePattern(config),
    '',
    generateOrchestratorSections(config, sourceContent)
  ];

  const skillMdContent = parts.join('\n');
  Write(`${skillDir}/SKILL.md`, skillMdContent);
}

Critical Quality Rules:

  1. SKILL.md must NOT contain full execution detail (agent prompts, bash commands)
  2. SKILL.md MUST contain Ref: markers pointing to phase files
  3. SKILL.md MUST contain Phase Reference Documents table
  4. Every phase mentioned in Execution Flow must have a corresponding phase file
  5. Data flow variables must be consistent across sections

Output

  • File: .claude/skills/{skillName}/SKILL.md
  • TodoWrite: Mark Phase 2 completed, Phase 3 in_progress

Next Phase

Return to orchestrator, then auto-continue to Phase 3: Phase Files Design.