Claude Code Workflow Studio Hands-On
Why This Article
Claude Code excels at single-shot code tasks, but when you need to chain "code review → update docs → submit PR → notify Slack" into an automated pipeline, Workflow Studio is the answer. I hit 7 real pitfalls during setup — here's the complete debrief.
What Is Claude Code Workflow Studio
Workflow Studio (claude workflow subcommand, v0.4+) is the official workflow editor for Claude Code:
- **Drag-and-drop YAML-based** step definitions (no GUI required)
- **Conditional branching**: `if/else` jumps based on command output
- **Loops**: `for_each` for batch file processing
- **Parallel execution**: multiple steps run concurrently
- **Context persistence**: variables shared between steps
Docs: https://docs.anthropic.com/en/docs/claude-code/workflows
Environment Setup
# Verify Claude Code version (requires ≥ 0.4.0)
claude --version
# Claude Code 0.4.2
# Initialize workflow project
claude workflow init my-ci-pipeline
cd my-ci-pipeline
# Creates:
# my-ci-pipeline/
# workflows/
# main.yaml
# steps/
# shared/
Core Config: Complete main.yaml Example
# workflows/main.yaml
name: ci-code-review
version: "1"
on:
push:
branches: [main, develop]
env:
REVIEWER: "@claude-code/config/team.yml"
SLACK_WEBHOOK: "${SLACK_WEBHOOK_URL}"
steps:
- id: lint
action: run
command: npm run lint
continue_on_error: false
- id: test
action: run
command: npm test
depends_on: [lint]
timeout: 300
- id: security-scan
action: run
command: npm audit --audit-level=moderate
depends_on: [lint]
- id: review
action: claude
prompt_file: "./steps/review-prompt.md"
model: claude-3-5-sonnet-20240620
max_tokens: 4096
depends_on: [test, security-scan]
- id: notify
action: webhook
url: "${SLACK_WEBHOOK}"
condition: "steps.review.status == 'success'"
payload:
text: "✅ PR #{context.pr_number} passed review"
🕳️ Pitfall 1: condition Uses `=` Instead of `==`
Wrong:
- id: notify
condition: steps.review.status = "success" # ❌ single = won't parse
Error:
Error: Workflow validation failed: condition expression error
Parse error at line 12, column 14: unexpected token '='
Fix:
- id: notify
condition: "steps.review.status == 'success'" # ✅ double ==
**Note**: The string value inside condition must use double quotes; single quotes cause a parse error.
---
🕳️ Pitfall 2: depends_on Order ≠ Execution Order
**Misconception**: depends_on: [A, B] runs A before B.
Reality: The downstream step triggers when both A and B finish — whichever finishes first doesn't matter.
- id: downstream
depends_on: [A, B]
# A and B run in parallel; downstream starts when both complete
For forced serial execution:
- id: A
action: run
command: echo "A done"
- id: B
depends_on: [A] # ✅ explicit dependency forces serial
action: run
command: echo "B runs after A"
---
🕳️ Pitfall 3: context Variable Not Persisting Across Steps
**Failure scenario**: Step 1 sets context.branch_name, Step 2 reads empty.
steps:
- id: get-branch
action: run
command: |
echo "BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)"
export:
- BRANCH_NAME # ❌ won't auto-export
**Result**: steps.show-branch gets an empty string.
**Fix**: Declare outputs explicitly:
- id: get-branch
action: run
command: |
echo "::set-output name=branch_name::$(git rev-parse --abbrev-ref HEAD)"
outputs:
branch_name: "${BRANCH_NAME}"
- id: show-branch
action: run
command: echo "${steps.get-branch.outputs.branch_name}" # ✅
---
🕳️ Pitfall 4: for_each Loop Doesn't Reference `${item}`
Wrong:
- id: lint-files
action: run
command: npm run lint
for_each:
files: ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"]
# ❌ item not referenced
Error:
Error: for_each item 'files' not referenced in command
Workflow stopped at step 'lint-files'
Fix:
- id: lint-files
action: run
command: |
npx eslint ${item} --max-warnings 0
for_each:
files: ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"]
Actual behavior: Runs 3 times, linting one glob pattern per iteration.
---
🕳️ Pitfall 5: webhook Action 10s Default Timeout Fails on Large Payloads
Scenario: 5000-character review result → POST to Slack → truncated.
- id: notify
action: webhook
url: "${SLACK_WEBHOOK}"
payload:
text: "${steps.review.outputs.summary}"
# ❌ default timeout: 10, large payload times out
Fix:
- id: notify
action: webhook
url: "${SLACK_WEBHOOK}"
timeout: 30 # ✅ explicit timeout
payload:
text: "${steps.review.outputs.summary}"
Rule of thumb: Payloads > 3000 chars should write to file first and pass a URL.
---
🕳️ Pitfall 6: model Alias Doesn't Accept Date-Stamped Versions
Wrong:
- id: review
action: claude
model: claude-3-5-sonnet-4-20250514 # ❌ date-stamped alias not supported
prompt_file: "./steps/review-prompt.md"
Error:
Error: Unknown model 'claude-3-5-sonnet-4-20250514'
Available: claude-opus-4-5, claude-3-5-sonnet-20240620, claude-3-haiku-20240307
Fix: Use the full version tag:
- id: review
action: claude
model: claude-3-5-sonnet-20240620 # ✅ full version tag
# Or use short alias:
# model: sonnet
---
🕳️ Pitfall 7: on.push.branches Glob Pattern Only Matches Literals
Wrong:
on:
push:
branches:
- "feature/**" # ❌ glob not supported
Actual behavior: All branches triggered the workflow, including main and develop.
Fix: List branches explicitly or filter inside the step:
on:
push:
branches:
- main
- develop
# Then in the step:
- id: feature-only
action: run
command: |
if [[ "${context.branch}" == feature/* ]]; then
echo "Running feature checks..."
fi
---
5-Step Production Checklist
1. **Version check**: claude --version → must be ≥ 0.4.0
2. **Syntax validation**: claude workflow validate workflows/main.yaml (no execution)
3. **Dry run**: claude workflow run --dry-run ci-code-review
4. **Single-step test**: claude workflow step test lint
5. **Log audit**: claude workflow logs --tail 100
Workflow Studio vs Traditional Scripts: When to Use Which
| Scenario | Tool |
|---|---|
| Single code generation/refactor | Claude Code direct |
| Multi-step with persistent state | Workflow Studio |
| GUI drag-and-drop needed | n8n / Make |
| Complex branching + loops | Workflow Studio |
| Simple cron jobs | Cron + Shell |
| Visual monitoring UI | n8n (built-in UI) |
Conclusion
Workflow Studio upgrades Claude Code from single-shot interactions to automated pipelines. The key is getting the YAML right — conditions use ==, loops reference ${item}, models need full version tags. The 7 pitfalls above cost me enough time to write 3 production workflows; this checklist will save you that detour.
Related Reading:
👉 Join MiniMax Token Plan: AI coding acceleration for businesses
👉 Join Zhipu Coding Plan: GLM-4.6/GLM-5 coding packages, China-stable, pay-per-token unlimited
👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: