feat: add workflow-tune skill for multi-step workflow pipeline optimization

New skill that executes workflow step chains sequentially, inspects artifacts,
analyzes quality via ccw cli resume chain (Gemini), and generates optimization
reports. Supports 4 input formats: pipe-separated commands, comma-separated
skills, JSON file, and natural language with semantic decomposition.

Key features:
- Orchestrator + 5-phase progressive loading architecture
- Intent-to-tool mapping with ambiguity resolution for NL input
- Command document generation with pre-execution confirmation loop
- Per-step analysis + cross-step synthesis via resume chain
- Auto-fix with user confirmation safety gate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
catlog22
2026-03-20 14:55:47 +08:00
parent 26a7371a20
commit d5b6480528
9 changed files with 2187 additions and 0 deletions

View File

@@ -0,0 +1,318 @@
# Phase 3: Analyze Step
> **COMPACT SENTINEL [Phase 3: Analyze Step]**
> This phase contains 5 execution steps (Step 3.1 -- 3.5).
> If you can read this sentinel but cannot find the full Step protocol below, context has been compressed.
> Recovery: `Read("phases/03-step-analyze.md")`
Analyze a completed step's artifacts and quality using `ccw cli --tool gemini --mode analysis`. Uses `--resume` to maintain context across step analyses, building a continuous analysis chain.
## Objective
- Inspect step artifacts (file list, content, quality signals)
- Build analysis prompt with step context + prior process log
- Execute via ccw cli Gemini with resume chain
- Parse analysis results → write step-{N}-analysis.md
- Append findings to process-log.md
- Return updated session ID for resume chain
## Execution
### Step 3.1: Inspect Artifacts
```javascript
const stepIdx = currentStepIndex;
const step = state.steps[stepIdx];
const stepDir = `${state.work_dir}/steps/step-${stepIdx + 1}`;
// Read artifacts manifest
const manifest = JSON.parse(Read(`${stepDir}/artifacts-manifest.json`));
// Build artifact summary based on analysis depth
let artifactSummary = '';
if (state.analysis_depth === 'quick') {
// Quick: just file list and sizes
artifactSummary = `Artifacts (${manifest.artifacts.length} files):\n` +
manifest.artifacts.map(a => `- ${a.path} (${a.type})`).join('\n');
} else {
// Standard/Deep: include file content summaries
artifactSummary = manifest.artifacts.map(a => {
const maxLines = state.analysis_depth === 'deep' ? 300 : 150;
try {
const content = Read(a.path, { limit: maxLines });
return `--- ${a.path} (${a.type}) ---\n${content}`;
} catch {
return `--- ${a.path} --- [unreadable]`;
}
}).join('\n\n');
// Deep: also include scratchpad files
if (state.analysis_depth === 'deep' && manifest.scratchpad_files?.length > 0) {
artifactSummary += '\n\n--- Scratchpad Files ---\n' +
manifest.scratchpad_files.slice(0, 5).map(f => {
const content = Read(f, { limit: 100 });
return `--- ${f} ---\n${content}`;
}).join('\n\n');
}
}
// Execution result summary
const execSummary = `Execution: ${step.execution.method} | ` +
`Success: ${step.execution.success} | ` +
`Duration: ${step.execution.duration_ms}ms | ` +
`Artifacts: ${manifest.artifacts.length} files`;
```
### Step 3.2: Build Prior Context
```javascript
// Build accumulated process log context for this analysis
const priorProcessLog = Read(`${state.work_dir}/process-log.md`);
// Build step chain context (what came before, what comes after)
const stepChainContext = state.steps.map((s, i) => {
const status = i < stepIdx ? 'completed' : i === stepIdx ? 'CURRENT' : 'pending';
const score = s.analysis?.quality_score || '-';
return `${i + 1}. [${status}] ${s.name} (${s.type}) — Quality: ${score}`;
}).join('\n');
// Previous step handoff context (if not first step)
let handoffContext = '';
if (stepIdx > 0) {
const prevStep = state.steps[stepIdx - 1];
const prevAnalysis = prevStep.analysis;
if (prevAnalysis) {
handoffContext = `PREVIOUS STEP OUTPUT SUMMARY:
Step "${prevStep.name}" produced ${prevStep.execution?.artifact_count || 0} artifacts.
Quality: ${prevAnalysis.quality_score}/100
Key outputs: ${prevAnalysis.key_outputs?.join(', ') || 'unknown'}
Handoff notes: ${prevAnalysis.handoff_notes || 'none'}`;
}
}
```
### Step 3.3: Construct Analysis Prompt
```javascript
// Ref: templates/step-analysis-prompt.md
const depthInstructions = {
quick: 'Provide brief assessment (3-5 bullet points). Focus on: execution success, output completeness, obvious issues.',
standard: 'Provide detailed assessment. Cover: execution quality, output completeness, artifact quality, step-to-step handoff readiness, potential issues.',
deep: 'Provide exhaustive assessment. Cover: execution quality, output completeness and correctness, artifact quality and structure, step-to-step handoff integrity, error handling, performance signals, architecture implications, edge cases.'
};
const analysisPrompt = `PURPOSE: Analyze the output of workflow step "${step.name}" (step ${stepIdx + 1}/${state.steps.length}) to assess quality, identify issues, and evaluate handoff readiness for the next step.
WORKFLOW CONTEXT:
Name: ${state.workflow_name}
Goal: ${state.workflow_context}
Step Chain:
${stepChainContext}
CURRENT STEP:
Name: ${step.name}
Type: ${step.type}
Command: ${step.command}
${step.success_criteria ? `Success Criteria: ${step.success_criteria}` : ''}
EXECUTION RESULT:
${execSummary}
${handoffContext}
STEP ARTIFACTS:
${artifactSummary}
ANALYSIS DEPTH: ${state.analysis_depth}
${depthInstructions[state.analysis_depth]}
TASK:
1. Assess step execution quality (did it succeed? complete output?)
2. Evaluate artifact quality (content correctness, completeness, format)
3. Check handoff readiness (can the next step consume this output?)
4. Identify issues, risks, or optimization opportunities
5. Rate overall step quality 0-100
EXPECTED OUTPUT (strict JSON, no markdown):
{
"quality_score": <0-100>,
"execution_assessment": {
"success": <true|false>,
"completeness": "<complete|partial|failed>",
"notes": "<brief assessment>"
},
"artifact_assessment": {
"count": <number>,
"quality": "<high|medium|low>",
"key_outputs": ["<main output 1>", "<main output 2>"],
"missing_outputs": ["<expected but missing>"]
},
"handoff_assessment": {
"ready": <true|false>,
"next_step_compatible": <true|false|null>,
"handoff_notes": "<what next step should know>"
},
"issues": [
{ "severity": "high|medium|low", "description": "<issue>", "suggestion": "<fix>" }
],
"optimization_opportunities": [
{ "area": "<area>", "description": "<opportunity>", "impact": "high|medium|low" }
],
"step_summary": "<1-2 sentence summary for process log>"
}
CONSTRAINTS: Be specific, reference artifact content where possible, output ONLY JSON`;
```
### Step 3.4: Execute via ccw cli Gemini with Resume
```javascript
function escapeForShell(str) {
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
}
// Build CLI command with optional resume
let cliCommand = `ccw cli -p "${escapeForShell(analysisPrompt)}" --tool gemini --mode analysis`;
// Resume from previous step's analysis session (maintains context chain)
if (state.analysis_session_id) {
cliCommand += ` --resume ${state.analysis_session_id}`;
}
Bash({
command: cliCommand,
run_in_background: true,
timeout: 300000 // 5 minutes
});
// STOP — wait for hook callback
```
### Step 3.5: Parse Results and Update Process Log
After CLI completes:
```javascript
const rawOutput = /* CLI output from callback */;
// Extract session ID from CLI output for resume chain
const sessionIdMatch = rawOutput.match(/\[CCW_EXEC_ID=([^\]]+)\]/);
if (sessionIdMatch) {
state.analysis_session_id = sessionIdMatch[1];
}
// Parse JSON
const jsonMatch = rawOutput.match(/\{[\s\S]*\}/);
let analysis;
if (jsonMatch) {
try {
analysis = JSON.parse(jsonMatch[0]);
} catch (e) {
// Fallback: extract score heuristically
const scoreMatch = rawOutput.match(/"quality_score"\s*:\s*(\d+)/);
analysis = {
quality_score: scoreMatch ? parseInt(scoreMatch[1]) : 50,
execution_assessment: { success: step.execution.success, completeness: 'unknown', notes: 'Parse failed' },
artifact_assessment: { count: manifest.artifacts.length, quality: 'unknown', key_outputs: [], missing_outputs: [] },
handoff_assessment: { ready: true, next_step_compatible: null, handoff_notes: '' },
issues: [{ severity: 'low', description: 'Analysis output parsing failed', suggestion: 'Review raw output' }],
optimization_opportunities: [],
step_summary: 'Analysis parsing failed — raw output saved for manual review'
};
}
} else {
analysis = {
quality_score: 50,
step_summary: 'No structured analysis output received'
};
}
// Write step analysis file
const stepAnalysisReport = `# Step ${stepIdx + 1} Analysis: ${step.name}
**Quality Score**: ${analysis.quality_score}/100
**Date**: ${new Date().toISOString()}
## Execution
- Success: ${analysis.execution_assessment?.success}
- Completeness: ${analysis.execution_assessment?.completeness}
- Notes: ${analysis.execution_assessment?.notes}
## Artifacts
- Count: ${analysis.artifact_assessment?.count}
- Quality: ${analysis.artifact_assessment?.quality}
- Key Outputs: ${analysis.artifact_assessment?.key_outputs?.join(', ') || 'N/A'}
- Missing: ${analysis.artifact_assessment?.missing_outputs?.join(', ') || 'None'}
## Handoff Readiness
- Ready: ${analysis.handoff_assessment?.ready}
- Next Step Compatible: ${analysis.handoff_assessment?.next_step_compatible}
- Notes: ${analysis.handoff_assessment?.handoff_notes}
## Issues
${(analysis.issues || []).map(i => `- [${i.severity}] ${i.description}${i.suggestion}`).join('\n') || 'None'}
## Optimization Opportunities
${(analysis.optimization_opportunities || []).map(o => `- [${o.impact}] ${o.area}: ${o.description}`).join('\n') || 'None'}
`;
Write(`${stepDir}/step-${stepIdx + 1}-analysis.md`, stepAnalysisReport);
// Append to process log
const processLogEntry = `
## Step ${stepIdx + 1}: ${step.name} — Score: ${analysis.quality_score}/100
**Command**: \`${step.command}\`
**Result**: ${analysis.execution_assessment?.completeness || 'unknown'} | ${analysis.artifact_assessment?.count || 0} artifacts
**Summary**: ${analysis.step_summary || 'No summary'}
**Issues**: ${(analysis.issues || []).filter(i => i.severity === 'high').map(i => i.description).join('; ') || 'None critical'}
**Handoff**: ${analysis.handoff_assessment?.handoff_notes || 'Ready'}
---
`;
// Append to process-log.md
const currentLog = Read(`${state.work_dir}/process-log.md`);
Write(`${state.work_dir}/process-log.md`, currentLog + processLogEntry);
// Update state
state.steps[stepIdx].analysis = {
quality_score: analysis.quality_score,
key_outputs: analysis.artifact_assessment?.key_outputs || [],
handoff_notes: analysis.handoff_assessment?.handoff_notes || '',
issue_count: (analysis.issues || []).length,
high_issues: (analysis.issues || []).filter(i => i.severity === 'high').length,
optimization_count: (analysis.optimization_opportunities || []).length,
analysis_file: `${stepDir}/step-${stepIdx + 1}-analysis.md`
};
state.steps[stepIdx].status = 'analyzed';
state.process_log_entries.push({
step_index: stepIdx,
step_name: step.name,
quality_score: analysis.quality_score,
summary: analysis.step_summary,
timestamp: new Date().toISOString()
});
state.updated_at = new Date().toISOString();
Write(`${state.work_dir}/workflow-state.json`, JSON.stringify(state, null, 2));
```
## Error Handling
| Error | Recovery |
|-------|----------|
| CLI timeout | Retry once without --resume (fresh session) |
| Resume session not found | Start fresh analysis session, continue |
| JSON parse fails | Extract score heuristically, save raw output |
| No output | Default score 50, minimal process log entry |
## Output
- **Files**: `step-{N}-analysis.md`, updated `process-log.md`
- **State**: `steps[stepIdx].analysis` updated, `analysis_session_id` updated
- **Next**: Phase 2 for next step, or Phase 4 (Synthesize) if all steps done