diff --git a/.claude/agents/dwe_deepwork-jobs.md b/.claude/agents/dwe_deepwork-jobs.md
new file mode 100644
index 00000000..fadfe156
--- /dev/null
+++ b/.claude/agents/dwe_deepwork-jobs.md
@@ -0,0 +1,302 @@
+---
+name: deepwork-jobs
+description: "DeepWork Jobs system - defining, implementing, and syncing multi-step AI workflows. Covers job.yml schema, step instructions, skill generation, hooks, and CLI commands."
+---
+
+# DeepWork Jobs System
+
+You are an expert on the DeepWork Jobs system - the framework for building
+reusable, multi-step AI workflows that integrate with AI coding assistants.
+
+## Core Concepts
+
+**Jobs** are complex, multi-step tasks defined once and executed many times.
+Each job consists of:
+
+- **job.yml**: The specification file defining the job's structure
+- **steps/**: Markdown files with detailed instructions for each step
+- **hooks/**: Optional validation scripts or prompts
+- **templates/**: Example file formats for outputs
+
+**Skills** are generated from jobs and loaded by AI platforms (Claude Code,
+Gemini CLI). Each step becomes a slash command the user can invoke.
+
+## Job Definition Structure
+
+Jobs live in `.deepwork/jobs/[job_name]/`:
+
+```
+.deepwork/jobs/
+└── competitive_research/
+ ├── job.yml
+ ├── steps/
+ │ ├── identify_competitors.md
+ │ └── research_competitors.md
+ ├── hooks/
+ │ └── validate_research.sh
+ └── templates/
+ └── competitor_profile.md
+```
+
+## The job.yml Schema
+
+Required fields:
+- `name`: lowercase with underscores, must start with letter (e.g., `competitive_research`)
+- `version`: semantic versioning X.Y.Z (e.g., `1.0.0`)
+- `summary`: concise description under 200 characters
+- `steps`: array of step definitions
+
+Optional fields:
+- `description`: detailed multi-line explanation of the job
+- `workflows`: named sequences that group steps
+- `changelog`: version history with changes
+
+## Step Definition Fields
+
+Each step in the `steps` array requires:
+- `id`: unique identifier, lowercase with underscores
+- `name`: human-readable name
+- `description`: what this step accomplishes
+- `instructions_file`: path to step markdown (e.g., `steps/identify.md`)
+- `outputs`: array of output files (string or object with `file` and optional `doc_spec`)
+
+Optional step fields:
+- `inputs`: user parameters or file inputs from previous steps
+- `dependencies`: array of step IDs this step requires
+- `exposed`: boolean, if true skill appears in user menus (default: false)
+- `quality_criteria`: array of criteria strings for validation
+- `agent`: agent type for delegation (e.g., `general-purpose`), adds `context: fork`
+- `hooks`: lifecycle hooks for validation (see Hooks section)
+- `stop_hooks`: deprecated, use `hooks.after_agent` instead
+
+## Input Types
+
+**User inputs** - parameters gathered from the user:
+```yaml
+inputs:
+ - name: market_segment
+ description: "Target market segment for research"
+```
+
+**File inputs** - outputs from previous steps:
+```yaml
+inputs:
+ - file: competitors_list.md
+ from_step: identify_competitors
+```
+
+Note: `from_step` must be listed in the step's `dependencies` array.
+
+## Output Types
+
+**Simple output** (string):
+```yaml
+outputs:
+ - competitors_list.md
+ - research/
+```
+
+**Output with doc spec** (object):
+```yaml
+outputs:
+ - file: reports/analysis.md
+ doc_spec: .deepwork/doc_specs/analysis_report.md
+```
+
+Doc specs define quality criteria for document outputs and are embedded in
+generated skills for validation.
+
+## Workflows
+
+Workflows group steps into named sequences. Steps not in any workflow are
+"standalone" and can be run independently.
+
+```yaml
+workflows:
+ - name: new_job
+ summary: "Create a new DeepWork job from scratch"
+ steps:
+ - define
+ - review_job_spec
+ - implement
+```
+
+**Concurrent steps** can run in parallel:
+```yaml
+steps:
+ - define
+ - [research_competitor_a, research_competitor_b] # run in parallel
+ - synthesize
+```
+
+## Lifecycle Hooks
+
+Hooks trigger at specific points during skill execution.
+
+**Supported events**:
+- `after_agent`: runs after agent finishes (quality validation)
+- `before_tool`: runs before tool use
+- `before_prompt`: runs when user submits prompt
+
+**Hook action types**:
+```yaml
+hooks:
+ after_agent:
+ - prompt: "Verify all criteria are met" # inline prompt
+ - prompt_file: hooks/quality_check.md # prompt from file
+ - script: hooks/run_tests.sh # shell script
+```
+
+Note: Claude Code currently only supports script hooks. Prompt hooks are
+parsed but not executed (documented limitation).
+
+## Skill Generation
+
+Running `deepwork sync` generates skills from job definitions:
+
+1. Parses all `job.yml` files in `.deepwork/jobs/`
+2. For each job, generates a **meta-skill** (entry point) and **step skills**
+3. Writes to platform-specific directories (e.g., `.claude/skills/`)
+
+**Claude Code skill structure**:
+- Meta-skill: `.claude/skills/[job_name]/SKILL.md`
+- Step skill: `.claude/skills/[job_name].[step_id]/SKILL.md`
+
+**Gemini CLI skill structure**:
+- Meta-skill: `.gemini/skills/[job_name]/index.toml`
+- Step skill: `.gemini/skills/[job_name]/[step_id].toml`
+
+## CLI Commands
+
+**Install DeepWork**:
+```bash
+deepwork install --platform claude
+```
+Creates `.deepwork/` structure, copies standard jobs, runs sync.
+
+**Sync skills**:
+```bash
+deepwork sync
+```
+Regenerates all skills from job definitions.
+
+**Hook execution** (internal):
+```bash
+deepwork hook check # check for pending rules
+deepwork hook run # execute pending rule actions
+```
+
+## Standard Jobs
+
+DeepWork ships with standard jobs that are auto-installed:
+
+- `deepwork_jobs`: Create and manage multi-step workflows
+ - `define`: Interactive job specification creation
+ - `review_job_spec`: Sub-agent validation against doc spec
+ - `implement`: Generate step files and sync
+ - `learn`: Improve instructions from execution learnings
+
+- `deepwork_rules`: Create file-change trigger rules
+ - `define`: Interactive rule creation
+
+Standard jobs live in `src/deepwork/standard_jobs/` and are copied to
+`.deepwork/jobs/` during installation.
+
+## Writing Step Instructions
+
+Step instruction files should include:
+
+1. **Objective**: Clear statement of what this step accomplishes
+2. **Task**: Detailed process with numbered steps
+3. **Inputs section**: What to gather/read before starting
+4. **Output format**: Examples of expected outputs
+5. **Quality criteria**: How to verify the step is complete
+
+Use the phrase "ask structured questions" when gathering user input -
+this triggers proper tooling for interactive prompts.
+
+## Template System
+
+Skills are generated using Jinja2 templates in `src/deepwork/templates/`:
+
+- `claude/skill-job-meta.md.jinja`: Meta-skill template
+- `claude/skill-job-step.md.jinja`: Step skill template
+- `gemini/skill-job-meta.toml.jinja`: Gemini meta-skill
+- `gemini/skill-job-step.toml.jinja`: Gemini step skill
+
+Template variables include job context, step metadata, inputs, outputs,
+hooks, quality criteria, and workflow position.
+
+## Platform Adapters
+
+The `AgentAdapter` class abstracts platform differences:
+
+- `ClaudeAdapter`: Claude Code with markdown skills in `.claude/skills/`
+- `GeminiAdapter`: Gemini CLI with TOML skills in `.gemini/skills/`
+
+Adapters handle:
+- Skill filename patterns
+- Hook event name mapping (e.g., `after_agent` -> `Stop` for Claude)
+- Settings file management
+- Permission syncing
+
+## Parser Dataclasses
+
+The `parser.py` module defines the job structure:
+
+- `JobDefinition`: Top-level job with name, version, steps, workflows
+- `Step`: Individual step with inputs, outputs, hooks, dependencies
+- `StepInput`: User parameter or file input
+- `OutputSpec`: Output file optionally with doc_spec reference
+- `HookAction`: Hook configuration (prompt, prompt_file, or script)
+- `Workflow`: Named step sequence
+- `WorkflowStepEntry`: Sequential or concurrent step group
+
+## Validation Rules
+
+The parser validates:
+- Dependencies reference existing steps
+- No circular dependencies
+- File inputs reference steps in dependencies
+- Workflow steps exist
+- No duplicate workflow names
+- Doc spec files exist (when referenced)
+
+## Common Patterns
+
+**Creating a new job**:
+1. Run `/deepwork_jobs` (or `/deepwork_jobs.define`)
+2. Answer structured questions about your workflow
+3. Review generated job.yml
+4. Run `/deepwork_jobs.implement` to generate step files
+5. Run `deepwork sync` to create skills
+
+**Adding a step to existing job**:
+1. Edit `.deepwork/jobs/[job_name]/job.yml`
+2. Add step definition with required fields
+3. Create instructions file in `steps/`
+4. Update workflow if applicable
+5. Run `deepwork sync`
+
+**Debugging sync issues**:
+- Check job.yml syntax with a YAML validator
+- Verify step IDs match filenames
+- Ensure dependencies form valid DAG
+- Check instructions files exist
+
+---
+
+## Topics
+
+Detailed documentation on specific subjects within this domain.
+
+$(deepwork topics --expert "deepwork-jobs")
+
+---
+
+## Learnings
+
+Hard-fought insights from real experiences.
+
+$(deepwork learnings --expert "deepwork-jobs")
+
diff --git a/.claude/agents/dwe_deepwork-rules.md b/.claude/agents/dwe_deepwork-rules.md
new file mode 100644
index 00000000..b0de00cd
--- /dev/null
+++ b/.claude/agents/dwe_deepwork-rules.md
@@ -0,0 +1,116 @@
+---
+name: deepwork-rules
+description: "DeepWork rules system - creating, organizing, and managing file-change rules that automatically trigger when specific files change during AI agent sessions."
+---
+
+# DeepWork Rules Expert
+
+Expert in creating and managing DeepWork rules - automated guardrails that trigger when specific files change during AI agent sessions.
+
+## What are Rules?
+
+Rules are markdown files with YAML frontmatter that define:
+- **Triggers**: File patterns that activate the rule when changed
+- **Safety conditions**: File patterns that prevent the rule from firing when also changed
+- **Instructions**: What the agent should do when the rule fires
+
+## Detection Modes
+
+### Trigger/Safety Mode (most common)
+- Fires when trigger patterns match AND no safety patterns match
+- Use for: "When X changes, check Y" rules
+- Example: When config changes, verify install docs
+
+### Set Mode (bidirectional correspondence)
+- Fires when files that should change together don't all change
+- Use for: Source/test pairing, model/migration sync
+- Example: `src/foo.py` and `tests/foo_test.py` should change together
+
+### Pair Mode (directional correspondence)
+- Fires when a trigger file changes but expected files don't
+- Changes to expected files alone do NOT trigger
+- Use for: API code requires documentation updates (but docs can update independently)
+
+## Rule File Format
+
+Rules live in `.deepwork/rules/` as markdown files:
+
+```markdown
+---
+name: Rule Name
+trigger: "pattern/**/*"
+safety: "optional/pattern"
+compare_to: base # optional
+---
+Instructions for when this rule fires.
+```
+
+## Pattern Syntax
+
+- `*` - Matches any characters within a single path segment
+- `**` - Matches any characters across multiple path segments (recursive)
+- `{name}` - Captures a single segment variable
+- `{path}` - Captures multiple segments variable
+
+## Comparison Modes
+
+- `base` (default) - Compare to merge-base with main/master
+- `default_tip` - Compare to current tip of default branch
+- `prompt` - Compare to state at start of each prompt
+
+## Action Types
+
+Rules support two action types:
+
+- **prompt** (default): Show instructions to the agent when the rule fires
+- **command**: Execute a shell command automatically
+
+Command action format:
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies will be automatically synced when pyproject.toml changes.
+```
+
+Command actions are useful for:
+- Auto-running `uv sync` when pyproject.toml changes
+- Running linters when source files change
+- Generating documentation when schemas change
+
+## When to Use Rules
+
+Rules are most valuable for:
+- Keeping documentation in sync with code
+- Enforcing security reviews for sensitive changes
+- Ensuring test coverage follows source changes
+- Maintaining team guidelines automatically
+
+---
+
+## Topics
+
+Detailed documentation on specific subjects within this domain.
+
+$(deepwork topics --expert "deepwork-rules")
+
+---
+
+## Learnings
+
+Hard-fought insights from real experiences.
+
+$(deepwork learnings --expert "deepwork-rules")
+
+---
+
+## Workflows
+
+Expert-owned multi-step workflows:
+
+- **define**: Create a new rule file that triggers when specified files change
+ - 1 steps
+ - Start: `/deepwork-rules.define`
diff --git a/.claude/agents/dwe_experts.md b/.claude/agents/dwe_experts.md
new file mode 100644
index 00000000..c05ec6c4
--- /dev/null
+++ b/.claude/agents/dwe_experts.md
@@ -0,0 +1,372 @@
+---
+name: experts
+description: "DeepWork experts system - creating domain knowledge collections with topics, learnings, and multi-step workflows. Covers expert.yml, workflow.yml schema, skill generation, and CLI commands."
+---
+
+# DeepWork Experts System
+
+You are an expert on the DeepWork experts system - the framework for building
+auto-improving collections of domain knowledge with embedded workflows.
+
+## Core Concepts
+
+**Experts** are structured knowledge repositories that grow smarter over time.
+Each expert represents deep knowledge in a specific domain and consists of:
+
+- **Core expertise**: Foundational knowledge captured in expert.yml
+- **Topics**: Detailed documentation on specific subjects
+- **Learnings**: Hard-fought insights from real experiences
+- **Workflows**: Multi-step task sequences that can be invoked as skills
+
+## Expert Structure
+
+Experts live in `.deepwork/experts/[folder-name]/`:
+
+```
+.deepwork/experts/
+└── rails_activejob/
+ ├── expert.yml
+ ├── topics/
+ │ └── retry_handling.md
+ ├── learnings/
+ │ └── job_errors_not_going_to_sentry.md
+ └── workflows/
+ └── debug_jobs/
+ ├── workflow.yml
+ └── steps/
+ └── analyze.md
+```
+
+The **expert name** derives from the folder name with spaces/underscores becoming
+dashes: `rails_activejob` → `rails-activejob`.
+
+## Writing Good expert.yml
+
+The expert.yml has two key fields:
+
+### discovery_description
+A concise description (1-3 sentences) that helps the system decide when to
+invoke this expert. Be specific about the domain and capabilities.
+
+Good: "Ruby on Rails ActiveJob - background job processing, retries, queues,
+and error handling in Rails applications."
+
+Bad: "Helps with Rails stuff."
+
+### full_expertise
+The core knowledge payload (~5 pages max). Structure it as:
+
+1. **Identity statement**: "You are an expert on..."
+2. **Core concepts**: Key ideas and mental models
+3. **Common patterns**: Typical approaches and solutions
+4. **Pitfalls to avoid**: Known gotchas and mistakes
+5. **Decision frameworks**: How to choose between options
+
+Write in second person ("You should...") as this becomes agent instructions.
+
+## Writing Good Topics
+
+Topics are deep dives into specific subjects within the domain.
+
+### When to create a topic
+- Subject needs more detail than fits in full_expertise
+- You find yourself repeatedly explaining something
+- A subject has enough nuance to warrant dedicated documentation
+
+### Topic file structure
+```markdown
+---
+name: Retry Handling
+keywords:
+ - retry
+ - exponential backoff
+ - dead letter queue
+last_updated: 2025-01-15
+---
+
+[Detailed content here]
+```
+
+### Keyword guidelines
+- Use topic-specific terms only
+- Avoid broad domain terms (don't use "Rails" in a Rails expert's topics)
+- Include synonyms and related terms users might search for
+- 3-7 keywords is typical
+
+## Writing Good Learnings
+
+Learnings capture hard-fought insights from real experiences - like mini
+retrospectives that prevent repeating mistakes.
+
+### When to create a learning
+- You solved a non-obvious problem
+- A debugging session revealed unexpected behavior
+- You discovered something that contradicts common assumptions
+- Future-you would benefit from this context
+
+### Learning file structure
+```markdown
+---
+name: Job errors not going to Sentry
+last_updated: 2025-01-20
+summarized_result: |
+ Sentry changed their standard gem for hooking into jobs.
+ SolidQueue still worked but ActiveJobKubernetes did not.
+---
+
+## Context
+What was happening and why it mattered...
+
+## Investigation
+What you tried and what you discovered...
+
+## Resolution
+How you fixed it and why that worked...
+
+## Key Takeaway
+The generalizable insight for future reference...
+```
+
+## Workflows
+
+**Workflows** are multi-step task sequences embedded within experts. Each workflow
+becomes a set of slash commands that users can invoke.
+
+### Workflow Structure
+
+Workflows live in `workflows/[workflow_name]/`:
+
+```
+workflows/
+└── new_job/
+ ├── workflow.yml
+ └── steps/
+ ├── define.md
+ └── implement.md
+```
+
+### The workflow.yml Schema
+
+Required fields:
+- `name`: lowercase with underscores, must match folder name
+- `version`: semantic versioning X.Y.Z (e.g., `1.0.0`)
+- `summary`: concise description under 200 characters
+- `steps`: array of step definitions
+
+Optional fields:
+- `description`: detailed multi-line explanation
+- `execution_order`: explicit step ordering with concurrent support
+- `changelog`: version history
+
+### Step Definition Fields
+
+Each step requires:
+- `id`: unique identifier within the expert (across all workflows)
+- `name`: human-readable name
+- `description`: what this step accomplishes
+- `instructions_file`: path to step markdown (e.g., `steps/define.md`)
+- `outputs`: array of output files
+
+Optional step fields:
+- `inputs`: user parameters or file inputs from previous steps
+- `dependencies`: array of step IDs this step requires
+- `exposed`: boolean, if true skill appears in user menus (default: false)
+- `quality_criteria`: array of criteria strings for validation
+- `agent`: agent type for delegation (e.g., `general-purpose`)
+- `hooks`: lifecycle hooks for validation
+
+### Input Types
+
+**User inputs** - parameters gathered from the user:
+```yaml
+inputs:
+ - name: market_segment
+ description: "Target market segment for research"
+```
+
+**File inputs** - outputs from previous steps:
+```yaml
+inputs:
+ - file: competitors_list.md
+ from_step: identify_competitors
+```
+
+### Concurrent Execution
+
+Steps can run in parallel using execution_order:
+```yaml
+execution_order:
+ - define
+ - [research_a, research_b] # concurrent
+ - synthesize
+```
+
+### Lifecycle Hooks
+
+Hooks trigger at specific points:
+- `after_agent`: runs after agent finishes (quality validation)
+- `before_tool`: runs before tool use
+- `before_prompt`: runs when user submits prompt
+
+Hook action types:
+```yaml
+hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+```
+
+Note: Claude Code currently only supports script hooks.
+
+## Skill Generation
+
+Running `deepwork sync` generates skills from expert definitions:
+
+1. Parses all experts in `.deepwork/experts/`
+2. For each workflow, generates step skills and a workflow meta-skill
+3. Writes to platform directories (e.g., `.claude/skills/`)
+
+**Two types of skills are generated:**
+
+1. **Step skills** - One per step, contains the actual instructions
+ - Naming: `{expert-name}.{step-id}`
+ - Example: `experts.define`, `rails-activejob.analyze`
+ - Path: `.claude/skills/[expert-name].[step-id]/SKILL.md`
+
+2. **Workflow meta-skills** - One per workflow, orchestrates the steps
+ - Naming: `{expert-name}.{workflow-name}`
+ - Example: `experts.new_workflow`, `experts.review_pr`
+ - Path: `.claude/skills/[expert-name].[workflow-name]/SKILL.md`
+ - Purpose: Entry point that routes to the first step and auto-continues through the workflow
+
+## CLI Commands
+
+**Install DeepWork**:
+```bash
+deepwork install --platform claude
+```
+Creates `.deepwork/` structure, copies standard experts, runs sync.
+
+**Sync skills**:
+```bash
+deepwork sync
+```
+Regenerates all skills from expert definitions.
+
+**List topics**:
+```bash
+deepwork topics --expert "expert-name"
+```
+
+**List learnings**:
+```bash
+deepwork learnings --expert "expert-name"
+```
+
+## How Experts Become Agents
+
+Running `deepwork sync` generates Claude agents in `.claude/agents/`:
+
+- Filename: `dwe_[expert-name].md`
+- Agent name: `[expert-name]`
+- Body: full_expertise + dynamic topic/learning lists
+
+The dynamic embedding ensures agents always access current topics and learnings:
+```
+$(deepwork topics --expert "expert-name")
+$(deepwork learnings --expert "expert-name")
+```
+
+## Standard Experts
+
+DeepWork ships with standard experts that are auto-installed:
+
+- `experts`: This expert - creating and managing experts with workflows
+ - `new_job` workflow: Define, review, and implement new workflows
+ - `learn` workflow: Extract learnings from workflow executions
+ - `review_pr` workflow: Expert-driven PR review
+
+- `deepwork_rules`: Create file-change trigger rules
+ - `define` workflow: Interactive rule creation
+
+## Writing Step Instructions
+
+Step instruction files should include:
+
+1. **Objective**: Clear statement of what this step accomplishes
+2. **Task**: Detailed process with numbered steps
+3. **Inputs section**: What to gather/read before starting
+4. **Output format**: Examples of expected outputs
+5. **Quality criteria**: How to verify the step is complete
+
+Use the phrase "ask structured questions" when gathering user input -
+this triggers proper tooling for interactive prompts.
+
+## Common Patterns
+
+**Creating a new workflow**:
+1. Run `/experts.define` (or just `/experts`)
+2. Answer structured questions about your workflow
+3. Review generated workflow.yml
+4. Run `/experts.implement` to generate step files
+5. Run `deepwork sync` to create skills
+
+**Adding a step to existing workflow**:
+1. Edit `.deepwork/experts/[expert]/workflows/[workflow]/workflow.yml`
+2. Add step definition with required fields
+3. Create instructions file in `steps/`
+4. Run `deepwork sync`
+
+## Evolution Strategy
+
+Experts should evolve through use:
+
+1. **Start minimal**: Begin with core expertise, add topics/learnings as needed
+2. **Capture immediately**: Document learnings right after solving problems
+3. **Refine periodically**: Review and consolidate as patterns emerge
+4. **Prune actively**: Remove outdated content, merge redundant topics
+
+## Naming Conventions
+
+### Expert folders
+- Use lowercase with underscores: `rails_activejob`, `social_marketing`
+- Be specific enough to be useful: `react_hooks` not just `react`
+
+### Workflow folders
+- Use lowercase with underscores: `new_job`, `review_pr`
+- Name describes the workflow's purpose
+
+### Step IDs
+- Must be unique within the expert (across all workflows)
+- Use lowercase with underscores: `define`, `implement`
+
+---
+
+## Topics
+
+Detailed documentation on specific subjects within this domain.
+
+$(deepwork topics --expert "experts")
+
+---
+
+## Learnings
+
+Hard-fought insights from real experiences.
+
+$(deepwork learnings --expert "experts")
+
+---
+
+## Workflows
+
+Expert-owned multi-step workflows:
+
+- **learn**: Analyze conversation history to improve workflow instructions and capture learnings
+ - 1 steps
+ - Start: `/experts.learn`
+- **review_pr**: Coordinate expert-driven PR review with iterative improvement cycles
+ - 3 steps
+ - Start: `/experts.check_relevance`
+- **new_workflow**: Create a new multi-step DeepWork workflow with spec definition, review, and implementation
+ - 3 steps
+ - Start: `/experts.define`
diff --git a/.claude/settings.json b/.claude/settings.json
index cf4e3c4c..893ea3a7 100644
--- a/.claude/settings.json
+++ b/.claude/settings.json
@@ -1,5 +1,8 @@
{
"permissions": {
+ "deny": [
+ "Bash(gh pr merge:*)"
+ ],
"allow": [
"WebFetch(domain:code.claude.com)",
"WebFetch(domain:www.anthropic.com)",
@@ -130,7 +133,21 @@
"Bash(deepwork:*)",
"Bash(.claude/hooks/commit_job_git_commit.sh:*)",
"Bash(./.deepwork/jobs/deepwork_jobs/make_new_job.sh:*)",
- "WebSearch"
+ "WebSearch",
+ "Skill(review_pr)",
+ "Skill(review_pr.check_relevance)",
+ "Skill(review_pr.deep_review)",
+ "Skill(review_pr.improve_and_rereview)",
+ "Skill(deepwork-rules.define)",
+ "Skill(experts.learn)",
+ "Skill(experts.review_pr)",
+ "Skill(experts.check_relevance)",
+ "Skill(experts.deep_review)",
+ "Skill(experts.improve_and_rereview)",
+ "Skill(experts.new_workflow)",
+ "Skill(experts.define)",
+ "Skill(experts.review_workflow_spec)",
+ "Skill(experts.implement)"
]
},
"hooks": {
@@ -165,6 +182,15 @@
"command": ".deepwork/jobs/deepwork_rules/hooks/user_prompt_submit.sh"
}
]
+ },
+ {
+ "matcher": "",
+ "hooks": [
+ {
+ "type": "command",
+ "command": ".deepwork/experts/deepwork_rules/hooks/user_prompt_submit.sh"
+ }
+ ]
}
],
"Stop": [
diff --git a/.claude/skills/add_platform/SKILL.md b/.claude/skills/add_platform/SKILL.md
index 474bb3a8..9c56af25 100644
--- a/.claude/skills/add_platform/SKILL.md
+++ b/.claude/skills/add_platform/SKILL.md
@@ -67,7 +67,7 @@ After each step completes:
### Handling Ambiguous Intent
-If user intent is unclear, use AskUserQuestion to clarify:
+If user intent is unclear, ask which option they want:
- Present available workflows and standalone skills as options
- Let user select the starting point
diff --git a/.claude/skills/commit/SKILL.md b/.claude/skills/commit/SKILL.md
index 3839ffbd..639b508f 100644
--- a/.claude/skills/commit/SKILL.md
+++ b/.claude/skills/commit/SKILL.md
@@ -63,7 +63,7 @@ After each step completes:
### Handling Ambiguous Intent
-If user intent is unclear, use AskUserQuestion to clarify:
+If user intent is unclear, ask which option they want:
- Present available workflows and standalone skills as options
- Let user select the starting point
diff --git a/.claude/skills/deepwork_rules.define/SKILL.md b/.claude/skills/deepwork-rules.define/SKILL.md
similarity index 77%
rename from .claude/skills/deepwork_rules.define/SKILL.md
rename to .claude/skills/deepwork-rules.define/SKILL.md
index 6a33878c..2439d950 100644
--- a/.claude/skills/deepwork_rules.define/SKILL.md
+++ b/.claude/skills/deepwork-rules.define/SKILL.md
@@ -1,20 +1,21 @@
---
-name: deepwork_rules.define
-description: "Creates a rule file that triggers when specified files change. Use when setting up documentation sync, code review requirements, or automated commands."
-user-invocable: false
+name: deepwork-rules.define
+description: "Create a new rule file that triggers when specified files change"
+context: fork
+agent: deepwork-rules
---
-# deepwork_rules.define
+# deepwork-rules.define
-**Standalone skill** - can be run anytime
+**Step 1/1** in **define** workflow
-> Creates file-change rules that enforce guidelines during AI sessions. Use when automating documentation sync or code review triggers.
+> Create a new rule file that triggers when specified files change
## Instructions
-**Goal**: Creates a rule file that triggers when specified files change. Use when setting up documentation sync, code review requirements, or automated commands.
+**Goal**: Create a new rule file that triggers when specified files change
# Define Rule
@@ -26,7 +27,7 @@ Create a new rule file in the `.deepwork/rules/` directory to enforce team guide
Guide the user through defining a new rule by asking structured questions. **Do not create the rule without first understanding what they want to enforce.**
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
### Step 1: Understand the Rule Purpose
@@ -159,6 +160,22 @@ Changed API: {trigger_files}
Update docs: {expected_files}
```
+**Format for Command Action (auto-run commands):**
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies will be automatically synced when pyproject.toml changes.
+```
+
+Command actions execute automatically instead of prompting the agent. Use for:
+- Dependency syncing (`uv sync`, `npm install`)
+- Linting/formatting (`ruff format`, `prettier`)
+- Code generation tasks
+
### Step 7: Verify the Rule
After creating the rule:
@@ -241,6 +258,18 @@ Changed API: {trigger_files}
Update: {expected_files}
```
+### Auto-Sync Dependencies (Command Action)
+`.deepwork/rules/auto-sync-deps.md`:
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies are automatically synced when pyproject.toml changes.
+```
+
## Output Format
### .deepwork/rules/{rule-name}.md
@@ -267,31 +296,11 @@ Rules are evaluated automatically when the agent finishes a task. The system:
You can mark a rule as addressed by including `Rule Name` in your response (replace Rule Name with the actual rule name from the `name` field). This tells the system you've already handled that rule's requirements.
-### Job Context
-
-Manages rules that automatically trigger when certain files change during an AI agent session.
-Rules help ensure that code changes follow team guidelines, documentation is updated,
-and architectural decisions are respected.
+### Workflow Context
-IMPORTANT: Rules are evaluated at the "Stop" hook, which fires when an agent finishes its turn.
-This includes when sub-agents complete their work. Rules are NOT evaluated immediately after
-each file edit - they batch up and run once at the end of the agent's response cycle.
-- Command action rules: Execute their command (e.g., `uv sync`) when the agent stops
-- Prompt action rules: Display instructions to the agent, blocking until addressed
-
-Rules are stored as individual markdown files with YAML frontmatter in the `.deepwork/rules/`
-directory. Each rule file specifies:
-- Detection mode: trigger/safety, set (bidirectional), or pair (directional)
-- Patterns: Glob patterns for matching files, with optional variable capture
-- Action type: prompt (default) to show instructions, or command to run a shell command
-- Instructions: Markdown content describing what the agent should do
-
-Example use cases:
-- Update installation docs when configuration files change
-- Require security review when authentication code is modified
-- Ensure API documentation stays in sync with API code
-- Enforce source/test file pairing
-- Auto-run `uv sync` when pyproject.toml changes (command action)
+Guide the user through defining a new DeepWork rule by asking structured questions
+about what they want to enforce, then creating the rule file with proper YAML
+frontmatter and markdown instructions.
## Required Inputs
@@ -302,10 +311,10 @@ Example use cases:
## Work Branch
-Use branch format: `deepwork/deepwork_rules-[instance]-YYYYMMDD`
+Use branch format: `deepwork/deepwork-rules-define-[instance]-YYYYMMDD`
- If on a matching work branch: continue using it
-- If on main/master: create new branch with `git checkout -b deepwork/deepwork_rules-[instance]-$(date +%Y%m%d)`
+- If on main/master: create new branch with `git checkout -b deepwork/deepwork-rules-define-[instance]-$(date +%Y%m%d)`
## Outputs
@@ -322,10 +331,9 @@ Use branch format: `deepwork/deepwork_rules-[instance]-YYYYMMDD`
## On Completion
1. Verify outputs are created
-2. Inform user: "define complete, outputs: .deepwork/rules/{rule-name}.md"
-
-This standalone skill can be re-run anytime.
+2. Inform user: "define step 1/1 complete, outputs: .deepwork/rules/{rule-name}.md"
+3. **define workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
---
-**Reference files**: `.deepwork/jobs/deepwork_rules/job.yml`, `.deepwork/jobs/deepwork_rules/steps/define.md`
\ No newline at end of file
+**Reference files**: `.deepwork/experts/deepwork_rules/workflows/define/workflow.yml`, `.deepwork/experts/deepwork_rules/workflows/define/steps/define.md`
\ No newline at end of file
diff --git a/.claude/skills/deepwork_jobs.define/SKILL.md b/.claude/skills/deepwork_jobs.define/SKILL.md
deleted file mode 100644
index f1469253..00000000
--- a/.claude/skills/deepwork_jobs.define/SKILL.md
+++ /dev/null
@@ -1,724 +0,0 @@
----
-name: deepwork_jobs.define
-description: "Creates a job.yml specification by gathering workflow requirements through structured questions. Use when starting a new multi-step workflow."
-user-invocable: false
-
----
-
-# deepwork_jobs.define
-
-**Step 1/3** in **new_job** workflow
-
-> Create a new DeepWork job from scratch through definition, review, and implementation
-
-> Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs.
-
-
-## Instructions
-
-**Goal**: Creates a job.yml specification by gathering workflow requirements through structured questions. Use when starting a new multi-step workflow.
-
-# Define Job Specification
-
-## Objective
-
-Create a `job.yml` specification file that defines the structure of a new DeepWork job by thoroughly understanding the user's workflow requirements through an interactive question-and-answer process.
-
-## Task
-
-Guide the user through defining a job specification by asking structured questions. **Do not attempt to create the specification without first fully understanding the user's needs.**
-
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
-
-The output of this step is **only** the `job.yml` file - a complete specification of the workflow. The actual step instruction files will be created in the next step (`implement`).
-
-### Step 1: Understand the Job Purpose
-
-Start by asking structured questions to understand what the user wants to accomplish:
-
-1. **What is the overall goal of this workflow?**
- - What complex task are they trying to accomplish?
- - What domain is this in? (e.g., research, marketing, development, reporting)
- - How often will they run this workflow?
-
-2. **What does success look like?**
- - What's the final deliverable or outcome?
- - Who is the audience for the output?
- - What quality criteria matter most?
-
-3. **What are the major phases?**
- - Ask them to describe the workflow at a high level
- - What are the distinct stages from start to finish?
- - Are there any dependencies between phases?
-
-### Step 1.5: Detect Document-Oriented Workflows
-
-**Check for document-focused patterns** in the user's description:
-- Keywords: "report", "summary", "document", "create", "monthly", "quarterly", "for stakeholders", "for leadership"
-- Final deliverable is a specific document (e.g., "AWS spending report", "competitive analysis", "sprint summary")
-- Recurring documents with consistent structure
-
-**If a document-oriented workflow is detected:**
-
-1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first to ensure consistent quality."
-
-2. Ask structured questions to understand if they want to:
- - Create a doc spec for this document
- - Use an existing doc spec (if any exist in `.deepwork/doc_specs/`)
- - Skip doc spec and proceed with simple outputs
-
-### Step 1.6: Define the Doc Spec (if needed)
-
-When creating a doc spec, gather the following information:
-
-1. **Document Identity**
- - What is the document called? (e.g., "Monthly AWS Spending Report")
- - Brief description of its purpose
- - Where should these documents be stored? (path patterns like `finance/aws-reports/*.md`)
-
-2. **Audience and Context**
- - Who reads this document? (target audience)
- - How often is it produced? (frequency)
-
-3. **Quality Criteria** (3-5 criteria, each with name and description)
-
- **Important**: Doc spec quality criteria define requirements for the **output document itself**, not the process of creating it. Focus on what the finished document must contain or achieve.
-
- Examples for a spending report:
- - **Visualization**: Must include charts showing spend breakdown by service
- - **Variance Analysis**: Must compare current month against previous with percentages
- - **Action Items**: Must include recommended cost optimization actions
-
- **Note**: When a doc spec is created for a step's output, the step should generally NOT have separate `quality_criteria` in the job.yml. The doc spec's criteria cover output quality. Only add step-level quality_criteria if there are essential process requirements (e.g., "must use specific tool"), and minimize these when possible.
-
-4. **Document Structure**
- - What sections should it have?
- - Any required elements (tables, charts, summaries)?
-
-### Step 1.7: Create the doc spec File (if needed)
-
-Create the doc spec file at `.deepwork/doc_specs/[doc_spec_name].md`:
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/doc_spec.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/doc_specs/job_spec.md` for a fully worked example (the doc spec for job.yml files).
-
-After creating the doc spec, proceed to Step 2 with the doc spec reference for the final step's output.
-
-### Step 2: Define Each Step
-
-For each major phase they mentioned, ask structured questions to gather details:
-
-1. **Step Purpose**
- - What exactly does this step accomplish?
- - What is the input to this step?
- - What is the output from this step?
-
-2. **Step Inputs**
- - What information is needed to start this step?
- - Does it need user-provided parameters? (e.g., topic, target audience)
- - Does it need files from previous steps?
- - What format should inputs be in?
-
-3. **Step Outputs**
- - What files or artifacts does this step produce?
- - What format should the output be in? (markdown, YAML, JSON, etc.)
- - Where should each output be saved? (filename/path)
- - Should outputs be organized in subdirectories? (e.g., `reports/`, `data/`, `drafts/`)
- - Will other steps need this output?
- - **Does this output have a doc spec?** If a doc spec was created in Step 1.6/1.7, reference it for the appropriate output
-
- #### Work Product Storage Guidelines
-
- **Key principle**: Job outputs belong in the main repository directory structure, not in dot-directories. The `.deepwork/` directory is for job definitions and configuration only.
-
- **Why this matters**:
- - **Version control**: Work products in the main repo are tracked by git and visible in PRs
- - **Discoverability**: Team members can find outputs without knowing about DeepWork internals
- - **Tooling compatibility**: IDEs, search tools, and CI/CD work naturally with standard paths
- - **Glob patterns**: Well-structured paths enable powerful file matching (e.g., `competitive_research/**/*.md`)
-
- **Good output path patterns**:
- ```
- competitive_research/competitors_list.md
- competitive_research/acme_corp/research.md
- operations/reports/2026-01/spending_analysis.md
- docs/api/endpoints.md
- ```
-
- **Avoid these patterns**:
- ```
- .deepwork/outputs/report.md # Hidden in dot-directory
- output.md # Too generic, no context
- research.md # Unclear which research
- temp/draft.md # Transient-sounding paths
- ```
-
- **Organizing multi-file outputs**:
- - Use the job name as a top-level folder when outputs are job-specific
- - Use parameterized paths for per-entity outputs: `competitive_research/[competitor_name]/`
- - Match existing project conventions when extending a codebase
-
- **When to include dates in paths**:
- - **Include date** for periodic outputs where each version is retained (e.g., monthly reports, quarterly reviews, weekly summaries). These accumulate over time and historical versions remain useful.
- ```
- operations/reports/2026-01/spending_analysis.md # Monthly report - keep history
- hr/employees/[employee_name]/quarterly_reviews/2026-Q1.pdf # Per-employee quarterly review
- ```
- - **Omit date** for current-state outputs that represent the latest understanding and get updated in place. Previous versions live in git history, not separate files.
- ```
- competitive_research/acme_corp/swot.md # Current SWOT - updated over time
- docs/architecture/overview.md # Living document
- ```
-
- **Supporting materials and intermediate outputs**:
- - Content generated in earlier steps to support the final output (research notes, data extracts, drafts) should be placed in a `_dataroom` folder that is a peer to the final output
- - Name the dataroom folder by replacing the file extension with `_dataroom`
- ```
- operations/reports/2026-01/spending_analysis.md # Final output
- operations/reports/2026-01/spending_analysis_dataroom/ # Supporting materials
- raw_data.csv
- vendor_breakdown.md
- notes.md
- ```
- - This keeps supporting materials organized and discoverable without cluttering the main output location
-
-4. **Step Dependencies**
- - Which previous steps must complete before this one?
- - Are there any ordering constraints?
-
-5. **Step Process** (high-level understanding)
- - What are the key activities in this step?
- - Are there any quality checks or validation needed?
- - What makes a good vs. bad output for this step?
-
-6. **Agent Delegation** (optional)
- - Should this step be executed by a specific agent type?
- - Use the `agent` field when the step should run in a forked context with a specific agent
- - When `agent` is set, the generated skill automatically includes `context: fork`
- - Available agent types:
- - `general-purpose` - Standard agent for multi-step tasks
-
- ```yaml
- steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
- ```
-
-**Note**: You're gathering this information to understand what instructions will be needed, but you won't create the instruction files yet - that happens in the `implement` step.
-
-#### Doc Spec-Aware Output Format
-
-When a step produces a document with a doc spec reference, use this format in job.yml:
-
-```yaml
-outputs:
- - file: reports/monthly_spending.md
- doc_spec: .deepwork/doc_specs/monthly_aws_report.md
-```
-
-The doc spec's quality criteria will automatically be included in the generated skill, ensuring consistent document quality.
-
-### Capability Considerations
-
-When defining steps, identify any that require specialized tools:
-
-**Browser Automation**: If any step involves web scraping, form filling, interactive browsing, UI testing, or research requiring website visits, ask the user what browser tools they have available. For Claude Code users, **Claude in Chrome** (Anthropic's browser extension) has been tested with DeepWork and is recommended for new users. Don't assume a default—confirm the tool before designing browser-dependent steps.
-
-### Step 3: Validate the Workflow
-
-After gathering information about all steps:
-
-1. **Review the flow**
- - Summarize the complete workflow
- - Show how outputs from one step feed into the next
- - Ask if anything is missing
-
-2. **Check for gaps**
- - Are there any steps where the input isn't clearly defined?
- - Are there any outputs that aren't used by later steps?
- - Are there circular dependencies?
-
-3. **Confirm details**
- - Job name (lowercase, underscores, descriptive)
- - Job summary (one clear sentence, max 200 chars)
- - Job description (detailed multi-line explanation)
- - Version number (start with 1.0.0)
-
-### Step 4: Define Quality Validation (Stop Hooks)
-
-For each step, consider whether it would benefit from **quality validation loops**. Stop hooks allow the AI agent to iteratively refine its work until quality criteria are met.
-
-**Ask structured questions about quality validation:**
-- "Are there specific quality criteria that must be met for this step?"
-- "Would you like the agent to validate its work before completing?"
-- "What would make you send the work back for revision?"
-
-**Stop hooks are particularly valuable for:**
-- Steps with complex outputs that need multiple checks
-- Steps where quality is critical (final deliverables)
-- Steps with subjective quality criteria that benefit from AI self-review
-
-**Three types of stop hooks are supported:**
-
-1. **Inline Prompt** (`prompt`) - Best for simple quality criteria
- ```yaml
- stop_hooks:
- - prompt: |
- Verify the output meets these criteria:
- 1. Contains at least 5 competitors
- 2. Each competitor has a description
- 3. Selection rationale is clear
- ```
-
-2. **Prompt File** (`prompt_file`) - For detailed/reusable criteria
- ```yaml
- stop_hooks:
- - prompt_file: hooks/quality_check.md
- ```
-
-3. **Script** (`script`) - For programmatic validation (tests, linting)
- ```yaml
- stop_hooks:
- - script: hooks/run_tests.sh
- ```
-
-**Multiple hooks can be combined:**
-```yaml
-stop_hooks:
- - script: hooks/lint_output.sh
- - prompt: "Verify the content is comprehensive and well-organized"
-```
-
-**Encourage prompt-based hooks** - They leverage the AI's ability to understand context and make nuanced quality judgments. Script hooks are best for objective checks (syntax, format, tests).
-
-### Step 5: Create the Job Directory and Specification
-
-Only after you have complete understanding, create the job directory and `job.yml` file:
-
-**First, create the directory structure** using the `make_new_job.sh` script:
-
-```bash
-.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
-```
-
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - For step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - For custom validation scripts
-- `.deepwork/jobs/[job_name]/templates/` - For example file formats
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Then create the job.yml file** at `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the NEW job you're creating, e.g., `competitive_research`)
-
-**Doc Spec**: See `.deepwork/doc_specs/job_spec.md` for the complete specification with quality criteria.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.example` for a fully worked example.
-
-**Important**:
-- Use lowercase with underscores for job name and step IDs
-- Ensure file inputs reference steps in dependencies
-- No circular dependencies
-- At least one output per step
-- The `summary` should be concise (max 200 chars)
-- The `description` should provide rich context for future refinement
-- Include a `changelog` section with an initial entry for version 1.0.0
-
-## Example Dialog
-
-```
-Claude: I'll help you define a new DeepWork job. Let's start by understanding what you're trying to accomplish.
-
-What complex task or workflow are you trying to create? What's the overall goal?
-
-User: I want to create competitive research reports for my company
-
-Claude: Great! Let's dig into that. A few questions:
-
-1. What's the final deliverable? Is it a written report, a presentation, or something else?
-2. Who is the audience? Your team, executives, clients?
-3. How comprehensive should these reports be?
-
-User: It's a written report for our product team. Should be comprehensive - analyzing 3-5 competitors.
-
-Claude: Perfect. Now let's think about the major phases of creating this report. What are the main stages you go through? For example:
-- Identifying which competitors to analyze?
-- Gathering information about them?
-- Analyzing the data?
-- Writing the report?
-
-Or is your process different?
-
-User: Yes, that's close. I would say:
-1. Identify competitors
-2. Research each one
-3. Create comparison analysis
-4. Write positioning recommendations
-
-Claude: Excellent! Now let's define each step in detail. Starting with "Identify competitors":
-
-1. What inputs do you need to start? Do you already have a market segment in mind, or do you need to specify that?
-2. What should the output look like? A list of competitor names? More detailed?
-3. How many competitors should be identified?
-
-User: I need to specify the market segment and product category as inputs. The output should be a markdown file with 5-7 competitors and brief descriptions of each.
-
-Claude: Perfect. A few more questions about that output:
-1. What should the file be called? Something like `competitors_list.md`?
-2. Should it go in the project root, or in a subdirectory like `research/` or `data/`?
-
-User: Just call it `competitors_list.md` in the project root.
-
-Claude: Great. Now for the "Research each one" step:
-1. Does it use the competitor list from the first step as input?
-2. What kind of research - product features, pricing, marketing, all of the above?
-3. What format should the research output be in?
-4. Where should that research be saved?
-
-[continues for each step...]
-
-[After gathering all information:]
-
-Claude: Let me summarize the workflow I've designed based on our conversation:
-
-**Job: competitive_research**
-Summary: Systematic competitive analysis workflow for product positioning
-
-Description:
-A comprehensive workflow for analyzing competitors in your market segment. This job helps product teams understand the competitive landscape by systematically identifying competitors, researching their offerings, creating comparison matrices, and developing strategic positioning recommendations.
-
-The workflow produces:
-- A vetted list of key competitors
-- Detailed research notes on each competitor
-- A comparison matrix highlighting key differentiators
-- Strategic positioning recommendations
-
-Designed for product teams conducting quarterly competitive analysis.
-
-**Steps:**
-
-Step 1: identify_competitors
-- Input: market_segment (user), product_category (user)
-- Output: competitors_list.md (5-7 competitors with descriptions)
-
-Step 2: research_competitors
-- Input: competitors_list.md (from step 1)
-- Output: research_notes.md (detailed findings for each competitor)
-- Depends on: identify_competitors
-
-Step 3: comparative_analysis
-- Input: research_notes.md (from step 2)
-- Output: comparison_matrix.md (side-by-side analysis)
-- Depends on: research_competitors
-
-Step 4: positioning_recommendations
-- Input: comparison_matrix.md (from step 3)
-- Output: positioning_report.md (strategic recommendations)
-- Depends on: comparative_analysis
-
-Does this accurately capture your workflow? Any changes needed?
-
-User: Yes, that's perfect!
-
-Claude: Great! Creating the job.yml specification now...
-
-[Creates .deepwork/jobs/competitive_research/job.yml with the complete spec]
-
-✓ Job specification created!
-
-**File created:**
-- .deepwork/jobs/competitive_research/job.yml
-
-**Next step:**
-Run `/deepwork_jobs.review_job_spec` to validate the specification against quality criteria.
-```
-
-## Important Guidelines
-
-1. **Focus on specification only** - Don't create instruction files yet
-2. **Ask structured questions** - Never skip the discovery phase; use the AskUserQuestion tool
-3. **Rich context in description** - This helps with future refinement
-4. **Validate understanding** - Summarize and confirm before creating
-5. **Use examples** - Help users understand what good specifications look like
-6. **Understand file organization** - Always ask structured questions about where outputs should be saved and if subdirectories are needed
-
-## Validation Rules
-
-Before creating the job.yml, ensure:
-- Job name: lowercase, underscores, no spaces
-- Version: semantic versioning (1.0.0)
-- Summary: concise, under 200 characters
-- Description: detailed, provides context
-- Step IDs: unique, descriptive, lowercase with underscores
-- Dependencies: must reference existing step IDs
-- File inputs: `from_step` must be in dependencies
-- At least one output per step
-- Outputs can be filenames (e.g., `report.md`) or paths (e.g., `reports/analysis.md`)
-- File paths in outputs should match where files will actually be created
-- No circular dependencies
-
-## Output Format
-
-### job.yml
-
-The complete YAML specification file (example shown in Step 5 above).
-
-**Location**: `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the new job being created)
-
-After creating the file:
-1. Inform the user that the specification is complete
-2. Recommend that they review the job.yml file
-3. Tell them to run `/deepwork_jobs.review_job_spec` next
-
-
-
-### Job Context
-
-Core commands for managing DeepWork jobs. These commands help you define new multi-step
-workflows and learn from running them.
-
-The `new_job` workflow guides you through defining and implementing a new job by
-asking structured questions about your workflow, understanding each step's inputs and outputs,
-reviewing the specification, and generating all necessary files.
-
-The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
-confusion or inefficiencies, and improves job instructions. It also captures bespoke
-learnings specific to the current run into AGENTS.md files in the working folder.
-
-
-## Required Inputs
-
-**User Parameters** - Gather from user before starting:
-- **job_purpose**: What complex task or workflow are you trying to accomplish?
-
-
-## Work Branch
-
-Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
-
-- If on a matching work branch: continue using it
-- If on main/master: create new branch with `git checkout -b deepwork/deepwork_jobs-[instance]-$(date +%Y%m%d)`
-
-## Outputs
-
-**Required outputs**:
-- `job.yml`
- **Doc Spec**: DeepWork Job Specification
- > YAML specification file that defines a multi-step workflow job for AI agents
- **Definition**: `.deepwork/doc_specs/job_spec.md`
- **Target Audience**: AI agents executing jobs and developers defining workflows
- **Quality Criteria**:
- 1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
- 2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
- 3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
- 4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
- 5. **Changelog Present**: Must include a changelog array with at least the initial version entry. Changelog should only include one entry per branch at most
- 6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
- 7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
- 8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
- 9. **Output Paths**: Outputs must be valid filenames or paths within the main repo directory structure, never in dot-directories like `.deepwork/`. Use specific, descriptive paths that lend themselves to glob patterns (e.g., `competitive_research/acme_corp/swot.md` or `operations/reports/2026-01/spending_analysis.md`). Parameterized paths like `[competitor_name]/` are encouraged for per-entity outputs. Avoid generic names (`output.md`, `analysis.md`) and transient-sounding paths (`temp/`, `draft.md`). Supporting materials for a final output should go in a peer `_dataroom` folder (e.g., `spending_analysis_dataroom/`).
- 10. **Concise Instructions**: The content of the file, particularly the description, must not have excessively redundant information. It should be concise and to the point given that extra tokens will confuse the AI.
-
-
- Example Document Structure
-
- ```markdown
- # DeepWork Job Specification: [job_name]
-
- A `job.yml` file defines a complete multi-step workflow that AI agents can execute. Each job breaks down a complex task into reviewable steps with clear inputs and outputs.
-
- ## Required Fields
-
- ### Top-Level Metadata
-
- ```yaml
- name: job_name # lowercase, underscores only
- version: "1.0.0" # semantic versioning
- summary: "Brief description" # max 200 characters
- description: | # detailed multi-line explanation
- [Explain what this workflow does, why it exists,
- what outputs it produces, and who should use it]
- ```
-
- ### Changelog
-
- ```yaml
- changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
- - version: "1.1.0"
- changes: "Added quality validation hooks"
- ```
-
- ### Steps Array
-
- ```yaml
- steps:
- - id: step_id # unique, lowercase_underscores
- name: "Human Readable Name"
- description: "What this step accomplishes"
- instructions_file: steps/step_id.md
- inputs:
- # User-provided inputs:
- - name: param_name
- description: "What the user provides"
- # File inputs from previous steps:
- - file: output.md
- from_step: previous_step_id
- outputs:
- - competitive_research/competitors_list.md # descriptive path
- - competitive_research/[competitor_name]/research.md # parameterized path
- # With doc spec reference:
- - file: competitive_research/final_report.md
- doc_spec: .deepwork/doc_specs/report_type.md
- dependencies:
- - previous_step_id # steps that must complete first
- ```
-
- ## Optional Fields
-
- ### Exposed Steps
-
- ```yaml
- steps:
- - id: learn
- exposed: true # Makes step available without running dependencies
- ```
-
- ### Agent Delegation
-
- When a step should be executed by a specific agent type, use the `agent` field. This automatically sets `context: fork` in the generated skill.
-
- ```yaml
- steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
- ```
-
- Available agent types:
- - `general-purpose` - Standard agent for multi-step tasks
-
- ### Quality Hooks
-
- ```yaml
- steps:
- - id: step_id
- hooks:
- after_agent:
- # Inline prompt for quality validation:
- - prompt: |
- Verify the output meets criteria:
- 1. [Criterion 1]
- 2. [Criterion 2]
- If ALL criteria are met, include `...`.
- # External prompt file:
- - prompt_file: hooks/quality_check.md
- # Script for programmatic validation:
- - script: hooks/run_tests.sh
- ```
-
- ### Stop Hooks (Legacy)
-
- ```yaml
- steps:
- - id: step_id
- stop_hooks:
- - prompt: "Validation prompt..."
- - prompt_file: hooks/check.md
- - script: hooks/validate.sh
- ```
-
- ## Validation Rules
-
- 1. **No circular dependencies**: Step A cannot depend on Step B if Step B depends on Step A
- 2. **File inputs require dependencies**: If a step uses `from_step: X`, then X must be in its dependencies
- 3. **Unique step IDs**: No two steps can have the same id
- 4. **Valid file paths**: Output paths must not contain invalid characters and should be in the main repo (not dot-directories)
- 5. **Instructions files exist**: Each `instructions_file` path should have a corresponding file created
-
- ## Example: Complete Job Specification
-
- ```yaml
- name: competitive_research
- version: "1.0.0"
- summary: "Systematic competitive analysis workflow"
- description: |
- A comprehensive workflow for analyzing competitors in your market segment.
- Helps product teams understand the competitive landscape through systematic
- identification, research, comparison, and positioning recommendations.
-
- Produces:
- - Vetted competitor list
- - Research notes per competitor
- - Comparison matrix
- - Strategic positioning report
-
- changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
-
- steps:
- - id: identify_competitors
- name: "Identify Competitors"
- description: "Identify 5-7 key competitors in the target market"
- instructions_file: steps/identify_competitors.md
- inputs:
- - name: market_segment
- description: "The market segment to analyze"
- - name: product_category
- description: "The product category"
- outputs:
- - competitive_research/competitors_list.md
- dependencies: []
-
- - id: research_competitors
- name: "Research Competitors"
- description: "Deep dive research on each identified competitor"
- instructions_file: steps/research_competitors.md
- inputs:
- - file: competitive_research/competitors_list.md
- from_step: identify_competitors
- outputs:
- - competitive_research/[competitor_name]/research.md
- dependencies:
- - identify_competitors
-
- - id: positioning_report
- name: "Positioning Report"
- description: "Strategic positioning recommendations"
- instructions_file: steps/positioning_report.md
- inputs:
- - file: competitive_research/[competitor_name]/research.md
- from_step: research_competitors
- outputs:
- - file: competitive_research/positioning_report.md
- doc_spec: .deepwork/doc_specs/positioning_report.md
- dependencies:
- - research_competitors
- ```
- ```
-
-
-
-## Guardrails
-
-- Do NOT skip prerequisite verification if this step has dependencies
-- Do NOT produce partial outputs; complete all required outputs before finishing
-- Do NOT proceed without required inputs; ask the user if any are missing
-- Do NOT modify files outside the scope of this step's defined outputs
-
-## On Completion
-
-1. Verify outputs are created
-2. Inform user: "new_job step 1/3 complete, outputs: job.yml"
-3. **Continue workflow**: Use Skill tool to invoke `/deepwork_jobs.review_job_spec`
-
----
-
-**Reference files**: `.deepwork/jobs/deepwork_jobs/job.yml`, `.deepwork/jobs/deepwork_jobs/steps/define.md`
\ No newline at end of file
diff --git a/.claude/skills/deepwork_jobs.implement/SKILL.md b/.claude/skills/deepwork_jobs.implement/SKILL.md
deleted file mode 100644
index a0c1d388..00000000
--- a/.claude/skills/deepwork_jobs.implement/SKILL.md
+++ /dev/null
@@ -1,330 +0,0 @@
----
-name: deepwork_jobs.implement
-description: "Generates step instruction files and syncs slash commands from the job.yml specification. Use after job spec review passes."
-user-invocable: false
-
----
-
-# deepwork_jobs.implement
-
-**Step 3/3** in **new_job** workflow
-
-> Create a new DeepWork job from scratch through definition, review, and implementation
-
-> Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs.
-
-## Prerequisites (Verify First)
-
-Before proceeding, confirm these steps are complete:
-- `/deepwork_jobs.review_job_spec`
-
-## Instructions
-
-**Goal**: Generates step instruction files and syncs slash commands from the job.yml specification. Use after job spec review passes.
-
-# Implement Job Steps
-
-## Objective
-
-Generate the DeepWork job directory structure and instruction files for each step based on the validated `job.yml` specification from the review_job_spec step.
-
-## Task
-
-Read the `job.yml` specification file and create all the necessary files to make the job functional, including directory structure and step instruction files. Then sync the commands to make them available.
-
-### Step 1: Create Directory Structure Using Script
-
-Run the `make_new_job.sh` script to create the standard directory structure:
-
-```bash
-.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
-```
-
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - Step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - Custom validation scripts (with .gitkeep)
-- `.deepwork/jobs/[job_name]/templates/` - Example file formats (with .gitkeep)
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Note**: If the directory already exists (e.g., job.yml was created by define step), you can skip this step or manually create the additional directories:
-```bash
-mkdir -p .deepwork/jobs/[job_name]/hooks .deepwork/jobs/[job_name]/templates
-touch .deepwork/jobs/[job_name]/hooks/.gitkeep .deepwork/jobs/[job_name]/templates/.gitkeep
-```
-
-### Step 2: Read and Validate the Specification
-
-1. **Locate the job.yml file**
- - Read `.deepwork/jobs/[job_name]/job.yml` from the review_job_spec step
- - Parse the YAML content
-
-2. **Validate the specification**
- - Ensure it follows the schema (name, version, summary, description, steps)
- - Check that all dependencies reference existing steps
- - Verify no circular dependencies
- - Confirm file inputs match dependencies
-
-3. **Extract key information**
- - Job name, version, summary, description
- - List of all steps with their details
- - Understand the workflow structure
-
-### Step 3: Generate Step Instruction Files
-
-For each step in the job.yml, create a comprehensive instruction file at `.deepwork/jobs/[job_name]/steps/[step_id].md`.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example` for a fully worked example.
-
-**Available templates in `.deepwork/jobs/deepwork_jobs/templates/`:**
-- `job.yml.template` - Job specification structure
-- `step_instruction.md.template` - Step instruction file structure
-- `agents.md.template` - AGENTS.md file structure
-- `job.yml.example` - Complete job specification example
-- `step_instruction.md.example` - Complete step instruction example
-
-**Guidelines for generating instructions:**
-
-1. **Use the job description** - The detailed description from job.yml provides crucial context
-2. **Be specific** - Don't write generic instructions; tailor them to the step's purpose
-3. **Provide examples** - Show what good output looks like
-4. **Explain the "why"** - Help the user understand the step's role in the workflow
-5. **Quality over quantity** - Detailed, actionable instructions are better than vague ones
-6. **Align with stop hooks** - If the step has `stop_hooks` defined, ensure the quality criteria in the instruction file match the validation criteria in the hooks
-7. **Ask structured questions** - When a step has user inputs, the instructions MUST explicitly tell the agent to "ask structured questions" using the AskUserQuestion tool to gather that information. Never use generic phrasing like "ask the user" - always use "ask structured questions"
-
-### Handling Stop Hooks
-
-If a step in the job.yml has `stop_hooks` defined, the generated instruction file should:
-
-1. **Mirror the quality criteria** - The "Quality Criteria" section should match what the stop hooks will validate
-2. **Be explicit about success** - Help the agent understand when the step is truly complete
-3. **Include the promise pattern** - Mention that `✓ Quality Criteria Met` should be included when criteria are met
-
-**Example: If the job.yml has:**
-```yaml
-- id: research_competitors
- name: "Research Competitors"
- stop_hooks:
- - prompt: |
- Verify the research meets criteria:
- 1. Each competitor has at least 3 data points
- 2. Sources are cited
- 3. Information is current (within last year)
-```
-
-**The instruction file should include:**
-```markdown
-## Quality Criteria
-
-- Each competitor has at least 3 distinct data points
-- All information is sourced with citations
-- Data is current (from within the last year)
-- When all criteria are met, include `✓ Quality Criteria Met` in your response
-```
-
-This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing.
-
-### Using Supplementary Reference Files
-
-Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root.
-
-See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples.
-
-### Step 4: Verify job.yml Location
-
-Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define and review_job_spec steps should have created and validated it. If for some reason it's not there, you may need to create or move it.
-
-### Step 5: Sync Skills
-
-Run `deepwork sync` to generate the skills for this job:
-
-```bash
-deepwork sync
-```
-
-This will:
-- Parse the job definition
-- Generate skills for each step
-- Make the skills available in `.claude/skills/` (or appropriate platform directory)
-
-### Step 6: Consider Rules for the New Job
-
-After implementing the job, consider whether there are **rules** that would help enforce quality or consistency when working with this job's domain.
-
-**What are rules?**
-
-Rules are automated guardrails stored as markdown files in `.deepwork/rules/` that trigger when certain files change during an AI session. They help ensure:
-- Documentation stays in sync with code
-- Team guidelines are followed
-- Architectural decisions are respected
-- Quality standards are maintained
-
-**When to suggest rules:**
-
-Think about the job you just implemented and ask:
-- Does this job produce outputs that other files depend on?
-- Are there documentation files that should be updated when this job's outputs change?
-- Are there quality checks or reviews that should happen when certain files in this domain change?
-- Could changes to the job's output files impact other parts of the project?
-
-**Examples of rules that might make sense:**
-
-| Job Type | Potential Rule |
-|----------|----------------|
-| API Design | "Update API docs when endpoint definitions change" |
-| Database Schema | "Review migrations when schema files change" |
-| Competitive Research | "Update strategy docs when competitor analysis changes" |
-| Feature Development | "Update changelog when feature files change" |
-| Configuration Management | "Update install guide when config files change" |
-
-**How to offer rule creation:**
-
-If you identify one or more rules that would benefit the user, explain:
-1. **What the rule would do** - What triggers it and what action it prompts
-2. **Why it would help** - How it prevents common mistakes or keeps things in sync
-3. **What files it would watch** - The trigger patterns
-
-Then ask the user:
-
-> "Would you like me to create this rule for you? I can run `/deepwork_rules.define` to set it up."
-
-If the user agrees, invoke the `/deepwork_rules.define` command to guide them through creating the rule.
-
-**Example dialogue:**
-
-```
-Based on the competitive_research job you just created, I noticed that when
-competitor analysis files change, it would be helpful to remind you to update
-your strategy documentation.
-
-I'd suggest a rule like:
-- **Name**: "Update strategy when competitor analysis changes"
-- **Trigger**: `**/positioning_report.md`
-- **Action**: Prompt to review and update `docs/strategy.md`
-
-Would you like me to create this rule? I can run `/deepwork_rules.define` to set it up.
-```
-
-**Note:** Not every job needs rules. Only suggest them when they would genuinely help maintain consistency or quality. Don't force rules where they don't make sense.
-
-## Example Implementation
-
-For a complete worked example showing a job.yml and corresponding step instruction file, see:
-- **Job specification**: `.deepwork/jobs/deepwork_jobs/templates/job.yml.example`
-- **Step instruction**: `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example`
-
-## Important Guidelines
-
-1. **Read the spec carefully** - Understand the job's intent from the description
-2. **Generate complete instructions** - Don't create placeholder or stub files
-3. **Maintain consistency** - Use the same structure for all step instruction files
-4. **Provide examples** - Show what good output looks like
-5. **Use context** - The job description provides valuable context for each step
-6. **Be specific** - Tailor instructions to the specific step, not generic advice
-
-## Validation Before Sync
-
-Before running `deepwork sync`, verify:
-- All directories exist
-- `job.yml` is in place
-- All step instruction files exist (one per step)
-- No file system errors
-
-## Completion Checklist
-
-Before marking this step complete, ensure:
-- [ ] job.yml validated and copied to job directory
-- [ ] All step instruction files created
-- [ ] Each instruction file is complete and actionable
-- [ ] `deepwork sync` executed successfully
-- [ ] Skills generated in platform directory
-- [ ] Considered whether rules would benefit this job (Step 6)
-- [ ] If rules suggested, offered to run `/deepwork_rules.define`
-
-## Quality Criteria
-
-- Job directory structure is correct
-- All instruction files are complete (not stubs)
-- Instructions are specific and actionable
-- Output examples are provided in each instruction file
-- Quality criteria defined for each step
-- Steps with user inputs explicitly use "ask structured questions" phrasing
-- Sync completed successfully
-- Skills available for use
-- Thoughtfully considered relevant rules for the job domain
-
-
-### Job Context
-
-Core commands for managing DeepWork jobs. These commands help you define new multi-step
-workflows and learn from running them.
-
-The `new_job` workflow guides you through defining and implementing a new job by
-asking structured questions about your workflow, understanding each step's inputs and outputs,
-reviewing the specification, and generating all necessary files.
-
-The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
-confusion or inefficiencies, and improves job instructions. It also captures bespoke
-learnings specific to the current run into AGENTS.md files in the working folder.
-
-
-## Required Inputs
-
-
-**Files from Previous Steps** - Read these first:
-- `job.yml` (from `review_job_spec`)
-
-## Work Branch
-
-Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
-
-- If on a matching work branch: continue using it
-- If on main/master: create new branch with `git checkout -b deepwork/deepwork_jobs-[instance]-$(date +%Y%m%d)`
-
-## Outputs
-
-**Required outputs**:
-- `steps/` (directory)
-
-## Guardrails
-
-- Do NOT skip prerequisite verification if this step has dependencies
-- Do NOT produce partial outputs; complete all required outputs before finishing
-- Do NOT proceed without required inputs; ask the user if any are missing
-- Do NOT modify files outside the scope of this step's defined outputs
-
-## Quality Validation
-
-**Before completing this step, you MUST have your work reviewed against the quality criteria below.**
-
-Use a sub-agent (Haiku model) to review your work against these criteria:
-
-**Criteria (all must be satisfied)**:
-1. **Directory Structure**: Is `.deepwork/jobs/[job_name]/` created correctly?
-2. **Complete Instructions**: Are ALL step instruction files complete (not stubs or placeholders)?
-3. **Specific & Actionable**: Are instructions tailored to each step's purpose, not generic?
-4. **Output Examples**: Does each instruction file show what good output looks like?
-5. **Quality Criteria**: Does each instruction file define quality criteria for its outputs?
-6. **Ask Structured Questions**: Do step instructions that gather user input explicitly use the phrase "ask structured questions"?
-7. **Sync Complete**: Has `deepwork sync` been run successfully?
-8. **Commands Available**: Are the slash-commands generated in `.claude/commands/`?
-9. **Rules Considered**: Has the agent thought about whether rules would benefit this job? If relevant rules were identified, did they explain them and offer to run `/deepwork_rules.define`? Not every job needs rules - only suggest when genuinely helpful.
-**Review Process**:
-1. Once you believe your work is complete, spawn a sub-agent using Haiku to review your work against the quality criteria above
-2. The sub-agent should examine your outputs and verify each criterion is met
-3. If the sub-agent identifies valid issues, fix them
-4. Have the sub-agent review again until all valid feedback has been addressed
-5. Only mark the step complete when the sub-agent confirms all criteria are satisfied
-
-## On Completion
-
-1. Verify outputs are created
-2. Inform user: "new_job step 3/3 complete, outputs: steps/"
-3. **new_job workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
-
----
-
-**Reference files**: `.deepwork/jobs/deepwork_jobs/job.yml`, `.deepwork/jobs/deepwork_jobs/steps/implement.md`
\ No newline at end of file
diff --git a/.claude/skills/deepwork_jobs.learn/SKILL.md b/.claude/skills/deepwork_jobs.learn/SKILL.md
deleted file mode 100644
index 95e1c61f..00000000
--- a/.claude/skills/deepwork_jobs.learn/SKILL.md
+++ /dev/null
@@ -1,449 +0,0 @@
----
-name: deepwork_jobs.learn
-description: "Analyzes conversation history to improve job instructions and capture learnings. Use after running a job to refine it."
-
----
-
-# deepwork_jobs.learn
-
-**Standalone skill** - can be run anytime
-
-> Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs.
-
-
-## Instructions
-
-**Goal**: Analyzes conversation history to improve job instructions and capture learnings. Use after running a job to refine it.
-
-# Learn from Job Execution
-
-## Objective
-
-Think deeply about this task. Reflect on the current conversation to identify learnings from DeepWork job executions, improve job instructions with generalizable insights, and capture bespoke (run-specific) learnings in AGENTS.md files in the deepest common folder that would contain all work on the topic in the future.
-
-## Task
-
-Analyze the conversation history to extract learnings and improvements, then apply them appropriately:
-- **Generalizable learnings** → Update job instruction files
-- **Bespoke learnings** (specific to this run) → Add to AGENTS.md in the deepest common folder for the topic
-
-### Step 1: Analyze Conversation for Job Executions
-
-1. **Scan the conversation** for DeepWork slash commands that were run
- - Look for patterns like `/job_name.step_id`
- - Identify which jobs and steps were executed
- - Note the order of execution
-
-2. **Identify the target folder**
- - This should be the deepest common folder that would contain all work on the topic in the future
- - Should be clear from conversation history where work was done
- - If unclear, run `git diff` to see where changes were made on the branch
-
-3. **If no job was specified**, ask the user:
- - "Which DeepWork job would you like me to learn from?"
- - List available jobs from `.deepwork/jobs/`
-
-### Step 2: Identify Points of Confusion and Inefficiency
-
-Review the conversation for:
-
-1. **Confusion signals**
- - Questions the agent asked that shouldn't have been necessary
- - Misunderstandings about what a step required
- - Incorrect outputs that needed correction
- - Ambiguous instructions that led to wrong interpretations
-
-2. **Inefficiency signals**
- - Extra steps or iterations that were needed
- - Information that had to be repeated
- - Context that was missing from instructions
- - Dependencies that weren't clear
-
-3. **Error patterns**
- - Failed validations and why they failed
- - Quality criteria that were misunderstood
- - Edge cases that weren't handled
-
-4. **Success patterns**
- - What worked particularly well
- - Efficient approaches worth preserving
- - Good examples that could be added to instructions
-
-### Step 3: Classify Learnings
-
-For each learning identified, determine if it is:
-
-**Generalizable** (should improve instructions):
-- Would help ANY future run of this job
-- Addresses unclear or missing guidance
-- Fixes incorrect assumptions in instructions
-- Adds helpful examples or context
-- Examples:
- - "Step instructions should mention that X format is required"
- - "Quality criteria should include checking for Y"
- - "Add example of correct output format"
-
-**doc spec-Related** (should improve doc spec files):
-- Improvements to document quality criteria
-- Changes to document structure or format
-- Updated audience or frequency information
-- Examples:
- - "The report should include a summary table"
- - "Quality criterion 'Visualization' needs clearer requirements"
- - "Documents need a section for action items"
-
-**Bespoke** (should go in AGENTS.md):
-- Specific to THIS project/codebase/run
-- Depends on local conventions or structure
-- References specific files or paths
-- Would not apply to other uses of this job
-- Examples:
- - "In this codebase, API endpoints are in `src/api/`"
- - "This project uses camelCase for function names"
- - "The main config file is at `config/settings.yml`"
-
-### Step 3.5: Identify doc spec-Related Learnings
-
-Review the conversation for doc spec-related improvements:
-
-1. **Quality Criteria Changes**
- - Were any quality criteria unclear or insufficient?
- - Did the agent repeatedly fail certain criteria?
- - Are there new criteria that should be added?
-
-2. **Document Structure Changes**
- - Did the user request different sections?
- - Were parts of the document format confusing?
- - Should the example document be updated?
-
-3. **Metadata Updates**
- - Has the target audience changed?
- - Should frequency or path patterns be updated?
-
-**Signals for doc spec improvements:**
-- User asked for changes to document format
-- Repeated validation failures on specific criteria
-- Feedback about missing sections or information
-- Changes to how documents are organized/stored
-
-### Step 4: Update Job Instructions (Generalizable Learnings)
-
-For each generalizable learning:
-
-1. **Locate the instruction file**
- - Path: `.deepwork/jobs/[job_name]/steps/[step_id].md`
-
-2. **Make targeted improvements**
- - Add missing context or clarification
- - Include helpful examples
- - Clarify ambiguous instructions
- - Update quality criteria if needed
-
-3. **Keep instructions concise**
- - Avoid redundancy - don't repeat the same guidance in multiple places
- - Be direct - remove verbose explanations that don't add value
- - Prefer bullet points over paragraphs where appropriate
-
-4. **Preserve instruction structure**
- - Keep existing sections (Objective, Task, Process, Output Format, Quality Criteria)
- - Add to appropriate sections rather than restructuring
- - Maintain consistency with other steps
-
-5. **Track changes for changelog**
- - Note what was changed and why
- - Prepare changelog entry for job.yml
-
-### Step 4b: Extract Shared Content into Referenced Files
-
-Review all instruction files for the job and identify content that:
-- Appears in multiple step instructions (duplicated)
-- Is lengthy and could be extracted for clarity
-- Would benefit from being maintained in one place
-
-**Extract to shared files:**
-
-1. **Create shared files** in `.deepwork/jobs/[job_name]/steps/shared/`
- - `conventions.md` - Coding/formatting conventions used across steps
- - `examples.md` - Common examples referenced by multiple steps
- - `schemas.md` - Data structures or formats used throughout
-
-2. **Reference from instructions** using markdown includes or explicit references:
- ```markdown
- ## Conventions
-
- Follow the conventions defined in `shared/conventions.md`.
- ```
-
-3. **Benefits of extraction:**
- - Single source of truth - update once, applies everywhere
- - Shorter instruction files - easier to read and maintain
- - Consistent guidance across steps
-
-### Step 4.5: Update doc spec Files (doc spec-Related Learnings)
-
-If doc spec-related learnings were identified:
-
-1. **Locate the doc spec file**
- - Find doc spec references in job.yml outputs (look for `doc_spec: .deepwork/doc_specs/[doc_spec_name].md`)
- - doc spec files are at `.deepwork/doc_specs/[doc_spec_name].md`
-
-2. **Update quality_criteria array**
- - Add new criteria with name and description
- - Modify existing criteria descriptions for clarity
- - Remove criteria that are no longer relevant
-
-3. **Update example document**
- - Modify the markdown body to reflect structure changes
- - Ensure the example matches updated criteria
-
-4. **Update metadata as needed**
- - target_audience: If audience has changed
- - frequency: If production cadence has changed
- - path_patterns: If storage location has changed
-
-**Example doc spec update:**
-```yaml
-# Before
-quality_criteria:
- - name: Visualization
- description: Include charts
-
-# After
-quality_criteria:
- - name: Visualization
- description: Include Mermaid.js charts showing spend breakdown by service and month-over-month trend
-```
-
-### Step 5: Create/Update AGENTS.md (Bespoke Learnings)
-
-The AGENTS.md file captures project-specific knowledge that helps future agent runs.
-
-1. **Determine the correct location**
- - Place AGENTS.md in the deepest common folder that would contain all work on the topic in the future
- - This ensures the knowledge is available when working in that context
- - If uncertain, place at the project root
-
-2. **Use file references where possible**
- - Instead of duplicating information, reference source files
- - This keeps AGENTS.md in sync as the codebase evolves
- - Pattern: "See `path/to/file.ext` for [description]"
-
-3. **AGENTS.md structure**: See `.deepwork/jobs/deepwork_jobs/templates/agents.md.template` for the standard format.
-
-4. **Writing entries**
- - Be concise but specific
- - Always prefer file references over inline content
- - Use line numbers when referencing specific code: `file.ext:42`
- - Group related learnings together
-
-### Step 6: Update Job Version and Changelog
-
-If instruction files were modified:
-
-1. **Bump version in job.yml**
- - Patch version (0.0.x) for instruction improvements
- - Minor version (0.x.0) if quality criteria changed
-
-2. **Add changelog entry**
- ```yaml
- - version: "[new_version]"
- changes: "Improved [step] instructions based on execution learnings: [brief description]"
- ```
-
-### Step 7: Sync Skills
-
-**Run deepwork sync** (if instructions were modified)
-```bash
-deepwork sync
-```
-
-## File Reference Patterns
-
-When adding entries to AGENTS.md, prefer these patterns:
-
-**Good (references)**:
-```markdown
-- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
-- Error handling pattern: See `src/utils/errors.ts:15-30`
-- Configuration schema: Defined in `config/schema.json`
-```
-
-**Avoid (duplicating)**:
-```markdown
-- API endpoints should return JSON with this format: { status: ..., data: ... }
-- Error handling should use try/catch with custom error classes
-- Configuration requires these fields: name, version, ...
-```
-
-## Quality Criteria
-
-- Conversation has been analyzed for job executions
-- Points of confusion and inefficiency are identified
-- Learnings are correctly classified (generalizable vs bespoke)
-- Job instructions updated for generalizable improvements
-- Instructions are concise - no redundancy or unnecessary verbosity
-- Shared/lengthy content extracted into referenced files where appropriate
-- AGENTS.md created/updated with bespoke learnings
-- File references used instead of duplicating content
-- AGENTS.md is in the correct folder (the deepest common folder for the topic)
-- When all criteria are met, include `✓ Quality Criteria Met`
-
-## Example Dialog
-
-```
-User: /deepwork_jobs.learn
-
-Claude: I'll analyze this conversation for learnings from DeepWork job executions.
-
-**Conversation Analysis**
-
-I found the following job executions:
-1. `/competitive_research.identify_competitors` - Completed
-2. `/competitive_research.research_competitors` - Completed with 2 iterations
-3. `/competitive_research.comparative_analysis` - Completed
-
-**Identified Issues**
-
-1. **Confusion in step 2**: The instructions didn't specify that competitor research should include pricing information. This caused an extra iteration.
-
-2. **Missing context**: The agent had to ask about the industry focus, which should have been carried forward from step 1.
-
-**Classification**
-
-- Generalizable: Add pricing to research requirements in instructions
-- Bespoke: This project focuses on B2B SaaS in the HR tech space
-
-**Actions Taken**
-
-1. Updated `.deepwork/jobs/competitive_research/steps/research_competitors.md`:
- - Added "Include pricing information (tiers, models, approximate ranges)" to research requirements
- - Added note about carrying forward industry context from previous step
-
-2. Created `./competitive_research_output/AGENTS.md`:
- ```markdown
- # Project Context for Competitive Research
-
- ## Job-Specific Context
-
- ### competitive_research
-
- #### Industry Focus
- - Domain: B2B SaaS, HR Technology sector
- - See `identify_competitors_output.md` for market definition
-
- #### Competitors Identified
- - Reference: `competitor_list.md` for full details
-
- ## Last Updated
- - Date: 2024-01-15
- - From conversation about: Initial competitive analysis run
- ```
-
-3. Updated job.yml version to 1.0.1 with changelog entry
-
-4. Ran `deepwork sync`
-
-**Summary**
-
-Updated job instructions and created AGENTS.md with bespoke learnings.
-```
-
-## Handling Edge Cases
-
-**No job executions found**:
-- Ask user which job they'd like to analyze
-- Or offer to review available jobs
-
-**Multiple jobs executed**:
-- Analyze each job separately
-- Create separate AGENTS.md entries or files as appropriate
-
-**AGENTS.md already exists**:
-- Read existing content
-- Append new learnings to appropriate sections
-- Update "Last Updated" section
-
-**No issues found**:
-- Document what worked well
-- Consider if any successful patterns should be added to instructions as examples
-
-**Sensitive information**:
-- Never include secrets, credentials, or PII in AGENTS.md
-- Reference config files instead of including values
-
-
-### Job Context
-
-Core commands for managing DeepWork jobs. These commands help you define new multi-step
-workflows and learn from running them.
-
-The `new_job` workflow guides you through defining and implementing a new job by
-asking structured questions about your workflow, understanding each step's inputs and outputs,
-reviewing the specification, and generating all necessary files.
-
-The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
-confusion or inefficiencies, and improves job instructions. It also captures bespoke
-learnings specific to the current run into AGENTS.md files in the working folder.
-
-
-## Required Inputs
-
-**User Parameters** - Gather from user before starting:
-- **job_name**: Name of the job that was run (optional - will auto-detect from conversation)
-
-
-## Work Branch
-
-Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
-
-- If on a matching work branch: continue using it
-- If on main/master: create new branch with `git checkout -b deepwork/deepwork_jobs-[instance]-$(date +%Y%m%d)`
-
-## Outputs
-
-**Required outputs**:
-- `AGENTS.md`
-
-## Guardrails
-
-- Do NOT skip prerequisite verification if this step has dependencies
-- Do NOT produce partial outputs; complete all required outputs before finishing
-- Do NOT proceed without required inputs; ask the user if any are missing
-- Do NOT modify files outside the scope of this step's defined outputs
-
-## Quality Validation
-
-**Before completing this step, you MUST have your work reviewed against the quality criteria below.**
-
-Use a sub-agent (Haiku model) to review your work against these criteria:
-
-**Criteria (all must be satisfied)**:
-1. **Conversation Analyzed**: Did the agent review the conversation for DeepWork job executions?
-2. **Confusion Identified**: Did the agent identify points of confusion, errors, or inefficiencies?
-3. **Instructions Improved**: Were job instructions updated to address identified issues?
-4. **Instructions Concise**: Are instructions free of redundancy and unnecessary verbosity?
-5. **Shared Content Extracted**: Is lengthy/duplicated content extracted into referenced files?
-6. **doc spec Reviewed (if applicable)**: For jobs with doc spec outputs, were doc spec-related learnings identified?
-7. **doc spec Updated (if applicable)**: Were doc spec files updated with improved quality criteria or structure?
-8. **Bespoke Learnings Captured**: Were run-specific learnings added to AGENTS.md?
-9. **File References Used**: Do AGENTS.md entries reference other files where appropriate?
-10. **Working Folder Correct**: Is AGENTS.md in the correct working folder for the job?
-11. **Generalizable Separated**: Are generalizable improvements in instructions, not AGENTS.md?
-12. **Sync Complete**: Has `deepwork sync` been run if instructions were modified?
-**Review Process**:
-1. Once you believe your work is complete, spawn a sub-agent using Haiku to review your work against the quality criteria above
-2. The sub-agent should examine your outputs and verify each criterion is met
-3. If the sub-agent identifies valid issues, fix them
-4. Have the sub-agent review again until all valid feedback has been addressed
-5. Only mark the step complete when the sub-agent confirms all criteria are satisfied
-
-## On Completion
-
-1. Verify outputs are created
-2. Inform user: "learn complete, outputs: AGENTS.md"
-
-This standalone skill can be re-run anytime.
-
----
-
-**Reference files**: `.deepwork/jobs/deepwork_jobs/job.yml`, `.deepwork/jobs/deepwork_jobs/steps/learn.md`
\ No newline at end of file
diff --git a/.claude/skills/deepwork_jobs.review_job_spec/SKILL.md b/.claude/skills/deepwork_jobs.review_job_spec/SKILL.md
deleted file mode 100644
index 51b8ed54..00000000
--- a/.claude/skills/deepwork_jobs.review_job_spec/SKILL.md
+++ /dev/null
@@ -1,496 +0,0 @@
----
-name: deepwork_jobs.review_job_spec
-description: "Reviews job.yml against quality criteria using a sub-agent for unbiased validation. Use after defining a job specification."
-user-invocable: false
-
----
-
-# deepwork_jobs.review_job_spec
-
-**Step 2/3** in **new_job** workflow
-
-> Create a new DeepWork job from scratch through definition, review, and implementation
-
-> Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs.
-
-## Prerequisites (Verify First)
-
-Before proceeding, confirm these steps are complete:
-- `/deepwork_jobs.define`
-
-## Instructions
-
-**Goal**: Reviews job.yml against quality criteria using a sub-agent for unbiased validation. Use after defining a job specification.
-
-# Review Job Specification
-
-## Objective
-
-Review the `job.yml` created in the define step against the doc spec quality criteria using a sub-agent for unbiased evaluation, then iterate on fixes until all criteria pass.
-
-## Why This Step Exists
-
-The define step focuses on understanding user requirements and creating a job specification. This review step ensures the specification meets quality standards before implementation. Using a sub-agent provides an unbiased "fresh eyes" review that catches issues the main agent might miss after being deeply involved in the definition process.
-
-## Task
-
-Use a sub-agent to review the job.yml against all 9 doc spec quality criteria, then fix any failed criteria. Repeat until all criteria pass.
-
-### Step 1: Read the Job Specification
-
-Read the `job.yml` file created in the define step:
-
-```
-.deepwork/jobs/[job_name]/job.yml
-```
-
-Also read the doc spec for reference:
-
-```
-.deepwork/doc_specs/job_spec.md
-```
-
-### Step 2: Spawn Review Sub-Agent
-
-Use the Task tool to spawn a sub-agent that will provide an unbiased review:
-
-```
-Task tool parameters:
-- subagent_type: "general-purpose"
-- model: "haiku"
-- description: "Review job.yml against doc spec"
-- prompt: [see below]
-```
-
-**Sub-agent prompt template:**
-
-```
-Review this job.yml against the following 9 quality criteria from the doc spec.
-
-For each criterion, respond with:
-- PASS or FAIL
-- If FAIL: specific issue and suggested fix
-
-## job.yml Content
-
-[paste the full job.yml content here]
-
-## Quality Criteria
-
-1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
-
-2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
-
-3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
-
-4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
-
-5. **Changelog Present**: Must include a changelog array with at least the initial version entry
-
-6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
-
-7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
-
-8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
-
-9. **Output Paths**: Outputs must be valid filenames or paths (e.g., `report.md` or `reports/analysis.md`)
-
-## Response Format
-
-Respond with a structured evaluation:
-
-### Overall: [X/9 PASS]
-
-### Criterion Results
-
-1. Valid Identifier: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-2. Semantic Version: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-[... continue for all 9 criteria ...]
-
-### Summary of Required Fixes
-
-[List any fixes needed, or "No fixes required - all criteria pass"]
-```
-
-### Step 3: Review Sub-Agent Findings
-
-Parse the sub-agent's response:
-
-1. **Count passing criteria** - How many of the 9 criteria passed?
-2. **Identify failures** - List specific criteria that failed
-3. **Note suggested fixes** - What changes does the sub-agent recommend?
-
-### Step 4: Fix Failed Criteria
-
-For each failed criterion, edit the job.yml to address the issue:
-
-**Common fixes by criterion:**
-
-| Criterion | Common Issue | Fix |
-|-----------|-------------|-----|
-| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
-| Semantic Version | Missing or invalid format | Set to `"1.0.0"` or fix format |
-| Concise Summary | Too long or vague | Shorten to <200 chars, be specific |
-| Rich Description | Single line or missing context | Add multi-line explanation with problem/process/outcome/users |
-| Changelog Present | Missing changelog | Add `changelog:` with initial version entry |
-| Complete Steps | Missing required fields | Add id, name, description, instructions_file, outputs, dependencies |
-| Valid Dependencies | Non-existent step or circular | Fix step ID reference or reorder dependencies |
-| Input Consistency | from_step not in dependencies | Add the referenced step to dependencies array |
-| Output Paths | Invalid characters or format | Use valid filename/path format |
-
-### Step 5: Re-Run Review (If Needed)
-
-If any criteria failed:
-
-1. **Spawn a new sub-agent** with the updated job.yml content
-2. **Review the new findings**
-3. **Fix any remaining issues**
-4. **Repeat until all 9 criteria pass**
-
-### Step 6: Confirm Completion
-
-When all 9 criteria pass:
-
-1. **Announce success**: "All 9 doc spec quality criteria pass."
-2. **List what was validated**:
- - Valid Identifier
- - Semantic Version
- - Concise Summary
- - Rich Description
- - Changelog Present
- - Complete Steps
- - Valid Dependencies
- - Input Consistency
- - Output Paths
-3. **Include the promise**: `Quality Criteria Met`
-4. **Guide to next step**: "Run `/deepwork_jobs.implement` to generate the step instruction files."
-
-## Example Review Cycle
-
-```
-Claude: Let me review the job.yml against the doc spec quality criteria.
-
-[Spawns sub-agent with haiku model]
-
-Sub-agent response:
-### Overall: 7/9 PASS
-
-### Criterion Results
-1. Valid Identifier: PASS
-2. Semantic Version: PASS
-3. Concise Summary: FAIL
- Issue: Summary is 245 characters, exceeds 200 char limit
- Fix: Shorten to "Competitive analysis workflow for product positioning"
-4. Rich Description: PASS
-5. Changelog Present: FAIL
- Issue: No changelog array present
- Fix: Add changelog with initial version entry
-6. Complete Steps: PASS
-7. Valid Dependencies: PASS
-8. Input Consistency: PASS
-9. Output Paths: PASS
-
-### Summary of Required Fixes
-1. Shorten summary to under 200 characters
-2. Add changelog array with initial version
-
-Claude: I found 2 issues. Let me fix them.
-
-[Edits job.yml to shorten summary and add changelog]
-
-Claude: Let me verify the fixes with another review.
-
-[Spawns new sub-agent]
-
-Sub-agent response:
-### Overall: 9/9 PASS
-
-### Summary of Required Fixes
-No fixes required - all criteria pass
-
-Claude: All 9 doc spec quality criteria pass.
-
-Quality Criteria Met
-
-**Next step:** Run `/deepwork_jobs.implement` to generate the step instruction files.
-```
-
-## Quality Criteria
-
-- **Sub-Agent Used**: A sub-agent was spawned to provide unbiased review (not just self-review)
-- **All doc spec Criteria Evaluated**: The sub-agent assessed all 9 quality criteria from the doc spec
-- **Findings Addressed**: All failed criteria were fixed by the main agent
-- **Validation Loop Complete**: The review-fix cycle continued until all criteria passed
-- **Promise Included**: The response includes `Quality Criteria Met` when complete
-
-## Output
-
-The validated `job.yml` file at `.deepwork/jobs/[job_name]/job.yml` that passes all 9 doc spec quality criteria.
-
-
-### Job Context
-
-Core commands for managing DeepWork jobs. These commands help you define new multi-step
-workflows and learn from running them.
-
-The `new_job` workflow guides you through defining and implementing a new job by
-asking structured questions about your workflow, understanding each step's inputs and outputs,
-reviewing the specification, and generating all necessary files.
-
-The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
-confusion or inefficiencies, and improves job instructions. It also captures bespoke
-learnings specific to the current run into AGENTS.md files in the working folder.
-
-
-## Required Inputs
-
-
-**Files from Previous Steps** - Read these first:
-- `job.yml` (from `define`)
-
-## Work Branch
-
-Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
-
-- If on a matching work branch: continue using it
-- If on main/master: create new branch with `git checkout -b deepwork/deepwork_jobs-[instance]-$(date +%Y%m%d)`
-
-## Outputs
-
-**Required outputs**:
-- `job.yml`
- **Doc Spec**: DeepWork Job Specification
- > YAML specification file that defines a multi-step workflow job for AI agents
- **Definition**: `.deepwork/doc_specs/job_spec.md`
- **Target Audience**: AI agents executing jobs and developers defining workflows
- **Quality Criteria**:
- 1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
- 2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
- 3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
- 4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
- 5. **Changelog Present**: Must include a changelog array with at least the initial version entry. Changelog should only include one entry per branch at most
- 6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
- 7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
- 8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
- 9. **Output Paths**: Outputs must be valid filenames or paths within the main repo directory structure, never in dot-directories like `.deepwork/`. Use specific, descriptive paths that lend themselves to glob patterns (e.g., `competitive_research/acme_corp/swot.md` or `operations/reports/2026-01/spending_analysis.md`). Parameterized paths like `[competitor_name]/` are encouraged for per-entity outputs. Avoid generic names (`output.md`, `analysis.md`) and transient-sounding paths (`temp/`, `draft.md`). Supporting materials for a final output should go in a peer `_dataroom` folder (e.g., `spending_analysis_dataroom/`).
- 10. **Concise Instructions**: The content of the file, particularly the description, must not have excessively redundant information. It should be concise and to the point given that extra tokens will confuse the AI.
-
-
- Example Document Structure
-
- ```markdown
- # DeepWork Job Specification: [job_name]
-
- A `job.yml` file defines a complete multi-step workflow that AI agents can execute. Each job breaks down a complex task into reviewable steps with clear inputs and outputs.
-
- ## Required Fields
-
- ### Top-Level Metadata
-
- ```yaml
- name: job_name # lowercase, underscores only
- version: "1.0.0" # semantic versioning
- summary: "Brief description" # max 200 characters
- description: | # detailed multi-line explanation
- [Explain what this workflow does, why it exists,
- what outputs it produces, and who should use it]
- ```
-
- ### Changelog
-
- ```yaml
- changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
- - version: "1.1.0"
- changes: "Added quality validation hooks"
- ```
-
- ### Steps Array
-
- ```yaml
- steps:
- - id: step_id # unique, lowercase_underscores
- name: "Human Readable Name"
- description: "What this step accomplishes"
- instructions_file: steps/step_id.md
- inputs:
- # User-provided inputs:
- - name: param_name
- description: "What the user provides"
- # File inputs from previous steps:
- - file: output.md
- from_step: previous_step_id
- outputs:
- - competitive_research/competitors_list.md # descriptive path
- - competitive_research/[competitor_name]/research.md # parameterized path
- # With doc spec reference:
- - file: competitive_research/final_report.md
- doc_spec: .deepwork/doc_specs/report_type.md
- dependencies:
- - previous_step_id # steps that must complete first
- ```
-
- ## Optional Fields
-
- ### Exposed Steps
-
- ```yaml
- steps:
- - id: learn
- exposed: true # Makes step available without running dependencies
- ```
-
- ### Agent Delegation
-
- When a step should be executed by a specific agent type, use the `agent` field. This automatically sets `context: fork` in the generated skill.
-
- ```yaml
- steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
- ```
-
- Available agent types:
- - `general-purpose` - Standard agent for multi-step tasks
-
- ### Quality Hooks
-
- ```yaml
- steps:
- - id: step_id
- hooks:
- after_agent:
- # Inline prompt for quality validation:
- - prompt: |
- Verify the output meets criteria:
- 1. [Criterion 1]
- 2. [Criterion 2]
- If ALL criteria are met, include `...`.
- # External prompt file:
- - prompt_file: hooks/quality_check.md
- # Script for programmatic validation:
- - script: hooks/run_tests.sh
- ```
-
- ### Stop Hooks (Legacy)
-
- ```yaml
- steps:
- - id: step_id
- stop_hooks:
- - prompt: "Validation prompt..."
- - prompt_file: hooks/check.md
- - script: hooks/validate.sh
- ```
-
- ## Validation Rules
-
- 1. **No circular dependencies**: Step A cannot depend on Step B if Step B depends on Step A
- 2. **File inputs require dependencies**: If a step uses `from_step: X`, then X must be in its dependencies
- 3. **Unique step IDs**: No two steps can have the same id
- 4. **Valid file paths**: Output paths must not contain invalid characters and should be in the main repo (not dot-directories)
- 5. **Instructions files exist**: Each `instructions_file` path should have a corresponding file created
-
- ## Example: Complete Job Specification
-
- ```yaml
- name: competitive_research
- version: "1.0.0"
- summary: "Systematic competitive analysis workflow"
- description: |
- A comprehensive workflow for analyzing competitors in your market segment.
- Helps product teams understand the competitive landscape through systematic
- identification, research, comparison, and positioning recommendations.
-
- Produces:
- - Vetted competitor list
- - Research notes per competitor
- - Comparison matrix
- - Strategic positioning report
-
- changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
-
- steps:
- - id: identify_competitors
- name: "Identify Competitors"
- description: "Identify 5-7 key competitors in the target market"
- instructions_file: steps/identify_competitors.md
- inputs:
- - name: market_segment
- description: "The market segment to analyze"
- - name: product_category
- description: "The product category"
- outputs:
- - competitive_research/competitors_list.md
- dependencies: []
-
- - id: research_competitors
- name: "Research Competitors"
- description: "Deep dive research on each identified competitor"
- instructions_file: steps/research_competitors.md
- inputs:
- - file: competitive_research/competitors_list.md
- from_step: identify_competitors
- outputs:
- - competitive_research/[competitor_name]/research.md
- dependencies:
- - identify_competitors
-
- - id: positioning_report
- name: "Positioning Report"
- description: "Strategic positioning recommendations"
- instructions_file: steps/positioning_report.md
- inputs:
- - file: competitive_research/[competitor_name]/research.md
- from_step: research_competitors
- outputs:
- - file: competitive_research/positioning_report.md
- doc_spec: .deepwork/doc_specs/positioning_report.md
- dependencies:
- - research_competitors
- ```
- ```
-
-
-
-## Guardrails
-
-- Do NOT skip prerequisite verification if this step has dependencies
-- Do NOT produce partial outputs; complete all required outputs before finishing
-- Do NOT proceed without required inputs; ask the user if any are missing
-- Do NOT modify files outside the scope of this step's defined outputs
-
-## Quality Validation
-
-**Before completing this step, you MUST have your work reviewed against the quality criteria below.**
-
-Use a sub-agent (Haiku model) to review your work against these criteria:
-
-**Criteria (all must be satisfied)**:
-1. **Sub-Agent Used**: Was a sub-agent spawned to provide unbiased review?
-2. **All doc spec Criteria Evaluated**: Did the sub-agent assess all 9 quality criteria?
-3. **Findings Addressed**: Were all failed criteria addressed by the main agent?
-4. **Validation Loop Complete**: Did the review-fix cycle continue until all criteria passed?
-**Review Process**:
-1. Once you believe your work is complete, spawn a sub-agent using Haiku to review your work against the quality criteria above
-2. The sub-agent should examine your outputs and verify each criterion is met
-3. If the sub-agent identifies valid issues, fix them
-4. Have the sub-agent review again until all valid feedback has been addressed
-5. Only mark the step complete when the sub-agent confirms all criteria are satisfied
-
-## On Completion
-
-1. Verify outputs are created
-2. Inform user: "new_job step 2/3 complete, outputs: job.yml"
-3. **Continue workflow**: Use Skill tool to invoke `/deepwork_jobs.implement`
-
----
-
-**Reference files**: `.deepwork/jobs/deepwork_jobs/job.yml`, `.deepwork/jobs/deepwork_jobs/steps/review_job_spec.md`
\ No newline at end of file
diff --git a/.claude/skills/deepwork_jobs/SKILL.md b/.claude/skills/deepwork_jobs/SKILL.md
deleted file mode 100644
index ec2526f5..00000000
--- a/.claude/skills/deepwork_jobs/SKILL.md
+++ /dev/null
@@ -1,84 +0,0 @@
----
-name: deepwork_jobs
-description: "Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs."
----
-
-# deepwork_jobs
-
-Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs.
-
-> **CRITICAL**: Always invoke steps using the Skill tool. Never copy/paste step instructions directly.
-
-Core commands for managing DeepWork jobs. These commands help you define new multi-step
-workflows and learn from running them.
-
-The `new_job` workflow guides you through defining and implementing a new job by
-asking structured questions about your workflow, understanding each step's inputs and outputs,
-reviewing the specification, and generating all necessary files.
-
-The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
-confusion or inefficiencies, and improves job instructions. It also captures bespoke
-learnings specific to the current run into AGENTS.md files in the working folder.
-
-
-## Workflows
-
-### new_job
-
-Create a new DeepWork job from scratch through definition, review, and implementation
-
-**Steps in order**:
-1. **define** - Creates a job.yml specification by gathering workflow requirements through structured questions. Use when starting a new multi-step workflow.
-2. **review_job_spec** - Reviews job.yml against quality criteria using a sub-agent for unbiased validation. Use after defining a job specification.
-3. **implement** - Generates step instruction files and syncs slash commands from the job.yml specification. Use after job spec review passes.
-
-**Start workflow**: `/deepwork_jobs.define`
-
-## Standalone Skills
-
-These skills can be run independently at any time:
-
-- **learn** - Analyzes conversation history to improve job instructions and capture learnings. Use after running a job to refine it.
- Command: `/deepwork_jobs.learn`
-
-
-## Execution Instructions
-
-### Step 1: Analyze Intent
-
-Parse any text following `/deepwork_jobs` to determine user intent:
-- "new_job" or related terms → start new_job workflow at `deepwork_jobs.define`
-- "learn" or related terms → run standalone skill `deepwork_jobs.learn`
-
-### Step 2: Invoke Starting Step
-
-Use the Skill tool to invoke the identified starting step:
-```
-Skill tool: deepwork_jobs.define
-```
-
-### Step 3: Continue Workflow Automatically
-
-After each step completes:
-1. Check if there's a next step in the workflow sequence
-2. Invoke the next step using the Skill tool
-3. Repeat until workflow is complete or user intervenes
-
-**Note**: Standalone skills do not auto-continue to other steps.
-
-### Handling Ambiguous Intent
-
-If user intent is unclear, use AskUserQuestion to clarify:
-- Present available workflows and standalone skills as options
-- Let user select the starting point
-
-## Guardrails
-
-- Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
-- Do NOT skip steps in a workflow unless the user explicitly requests it
-- Do NOT proceed to the next step if the current step's outputs are incomplete
-- Do NOT make assumptions about user intent; ask for clarification when ambiguous
-
-## Context Files
-
-- Job definition: `.deepwork/jobs/deepwork_jobs/job.yml`
\ No newline at end of file
diff --git a/.claude/skills/deepwork_rules/SKILL.md b/.claude/skills/deepwork_rules/SKILL.md
deleted file mode 100644
index 3de565a9..00000000
--- a/.claude/skills/deepwork_rules/SKILL.md
+++ /dev/null
@@ -1,83 +0,0 @@
----
-name: deepwork_rules
-description: "Creates file-change rules that enforce guidelines during AI sessions. Use when automating documentation sync or code review triggers."
----
-
-# deepwork_rules
-
-Creates file-change rules that enforce guidelines during AI sessions. Use when automating documentation sync or code review triggers.
-
-> **CRITICAL**: Always invoke steps using the Skill tool. Never copy/paste step instructions directly.
-
-Manages rules that automatically trigger when certain files change during an AI agent session.
-Rules help ensure that code changes follow team guidelines, documentation is updated,
-and architectural decisions are respected.
-
-IMPORTANT: Rules are evaluated at the "Stop" hook, which fires when an agent finishes its turn.
-This includes when sub-agents complete their work. Rules are NOT evaluated immediately after
-each file edit - they batch up and run once at the end of the agent's response cycle.
-- Command action rules: Execute their command (e.g., `uv sync`) when the agent stops
-- Prompt action rules: Display instructions to the agent, blocking until addressed
-
-Rules are stored as individual markdown files with YAML frontmatter in the `.deepwork/rules/`
-directory. Each rule file specifies:
-- Detection mode: trigger/safety, set (bidirectional), or pair (directional)
-- Patterns: Glob patterns for matching files, with optional variable capture
-- Action type: prompt (default) to show instructions, or command to run a shell command
-- Instructions: Markdown content describing what the agent should do
-
-Example use cases:
-- Update installation docs when configuration files change
-- Require security review when authentication code is modified
-- Ensure API documentation stays in sync with API code
-- Enforce source/test file pairing
-- Auto-run `uv sync` when pyproject.toml changes (command action)
-
-
-## Standalone Skills
-
-These skills can be run independently at any time:
-
-- **define** - Creates a rule file that triggers when specified files change. Use when setting up documentation sync, code review requirements, or automated commands.
- Command: `/deepwork_rules.define`
-
-
-## Execution Instructions
-
-### Step 1: Analyze Intent
-
-Parse any text following `/deepwork_rules` to determine user intent:
-- "define" or related terms → run standalone skill `deepwork_rules.define`
-
-### Step 2: Invoke Starting Step
-
-Use the Skill tool to invoke the identified starting step:
-```
-Skill tool: deepwork_rules.define
-```
-
-### Step 3: Continue Workflow Automatically
-
-After each step completes:
-1. Check if there's a next step in the workflow sequence
-2. Invoke the next step using the Skill tool
-3. Repeat until workflow is complete or user intervenes
-
-**Note**: Standalone skills do not auto-continue to other steps.
-
-### Handling Ambiguous Intent
-
-If user intent is unclear, use AskUserQuestion to clarify:
-- Present available steps as numbered options
-- Let user select the starting point
-
-## Guardrails
-
-- Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
-- Do NOT skip steps in a workflow unless the user explicitly requests it
-- Do NOT proceed to the next step if the current step's outputs are incomplete
-- Do NOT make assumptions about user intent; ask for clarification when ambiguous
-
-## Context Files
-
-- Job definition: `.deepwork/jobs/deepwork_rules/job.yml`
\ No newline at end of file
diff --git a/.claude/skills/experts.check_relevance/SKILL.md b/.claude/skills/experts.check_relevance/SKILL.md
new file mode 100644
index 00000000..62dad730
--- /dev/null
+++ b/.claude/skills/experts.check_relevance/SKILL.md
@@ -0,0 +1,179 @@
+---
+name: experts.check_relevance
+description: "Invoke all experts in parallel to assess if PR changes are relevant to their domain"
+context: fork
+agent: experts
+
+---
+
+# experts.check_relevance
+
+**Step 1/3** in **review_pr** workflow
+
+> Coordinate expert-driven PR review with iterative improvement cycles
+
+
+## Instructions
+
+**Goal**: Invoke all experts in parallel to assess if PR changes are relevant to their domain
+
+# Check Expert Relevance
+
+## Objective
+
+Invoke all available experts in parallel to determine which ones have relevant expertise for the PR changes. This filtering step ensures only applicable experts spend time on detailed review.
+
+## Task
+
+Assess which domain experts can meaningfully contribute to reviewing the current PR by having each expert examine the changes and report their relevance.
+
+### Process
+
+1. **Get PR metadata**
+
+ Get basic PR info for the output file:
+ ```bash
+ gh pr view --json number,title,headRefName
+ ```
+
+ This is needed for the output file header. The diff and file list will be
+ embedded directly in expert prompts using inline command expansion.
+
+2. **Discover available experts**
+
+ List all experts in the project:
+ ```bash
+ ls -1 .deepwork/experts/
+ ```
+
+ Read each expert's `discovery_description` from their `expert.yml` to understand their domain.
+
+3. **Invoke experts in parallel**
+
+ For each expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Use `$(command)` syntax in the prompt to embed command output directly when
+ the sub-agent is spawned. This avoids reading large diffs into the main
+ conversation context.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Review this PR to determine if the changes are relevant to your domain of expertise.
+
+ ## Changed Files
+
+ $(gh pr diff --name-only)
+
+ ## Diff Summary (first 200 lines)
+
+ $(gh pr diff | head -200)
+
+ ## Your Task
+
+ Based on your specific domain knowledge, respond with:
+ 1. RELEVANT or NOT_RELEVANT
+ 2. Brief justification (1-2 sentences) explaining why this does or does not fall within your expertise
+ 3. If relevant, which specific files/changes you can review from your expert perspective
+ ```
+
+ **Important**: Invoke ALL experts in parallel to minimize latency.
+
+4. **Collect and summarize responses**
+
+ Wait for all expert responses. Compile into the output file.
+
+## Output Format
+
+### pr_review/relevance_assessments.md
+
+Create this file with the assessment results:
+
+```markdown
+# PR Relevance Assessment
+
+**PR**: #[number] - [title]
+**Branch**: [branch_name]
+**Date**: [YYYY-MM-DD]
+
+## Changed Files
+
+- path/to/file1.py
+- path/to/file2.ts
+- ...
+
+## Expert Assessments
+
+### [expert-name-1]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+### [expert-name-2]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+...
+
+## Summary
+
+**Relevant experts**: [list of expert names marked RELEVANT]
+**Next step**: Run `/experts.deep_review` with these experts
+```
+
+## Quality Criteria
+
+- All experts in `.deepwork/experts/` were invoked in parallel
+- Each expert provided a yes/no relevance determination with justification
+- Relevant experts are clearly identified for the next step
+- PR metadata (number, title, branch) is captured
+- Changed files are listed for reference
+
+## Context
+
+This is the first phase of the expert-driven PR review workflow. By checking relevance first, we avoid wasting expert review time on changes outside their domain. The parallel invocation ensures this filtering step completes quickly even with many experts.
+
+The output file serves as input to the `deep_review` step, which will only invoke experts marked as RELEVANT.
+
+
+### Workflow Context
+
+Orchestrate a comprehensive PR review using domain experts:
+1. Check which experts have relevant expertise for the PR changes
+2. Have relevant experts perform deep code review
+3. Iteratively improve the PR based on feedback until all experts approve
+
+
+
+## Work Branch
+
+Use branch format: `deepwork/experts-review_pr-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-review_pr-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/relevance_assessments.md`
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "review_pr step 1/3 complete, outputs: pr_review/relevance_assessments.md"
+3. **Continue workflow**: Use Skill tool to invoke `/experts.deep_review`
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/review_pr/workflow.yml`, `.deepwork/experts/experts/workflows/review_pr/steps/check_relevance.md`
\ No newline at end of file
diff --git a/.claude/skills/experts.deep_review/SKILL.md b/.claude/skills/experts.deep_review/SKILL.md
new file mode 100644
index 00000000..6469bdde
--- /dev/null
+++ b/.claude/skills/experts.deep_review/SKILL.md
@@ -0,0 +1,261 @@
+---
+name: experts.deep_review
+description: "Invoke only relevant experts to perform detailed code review"
+user-invocable: false
+context: fork
+agent: experts
+
+---
+
+# experts.deep_review
+
+**Step 2/3** in **review_pr** workflow
+
+> Coordinate expert-driven PR review with iterative improvement cycles
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/experts.check_relevance`
+
+## Instructions
+
+**Goal**: Invoke only relevant experts to perform detailed code review
+
+# Deep Expert Review
+
+## Objective
+
+Have relevant domain experts perform thorough code review of the PR changes **specifically within their area of expertise**. Each expert produces detailed written feedback with specific suggestions for improvement, drawing on their domain knowledge.
+
+## Task
+
+Invoke only the experts marked as RELEVANT in the previous step to perform detailed code review, producing actionable feedback focused on their specific domain.
+
+### Process
+
+1. **Read the relevance assessments**
+
+ Read `pr_review/relevance_assessments.md` to identify:
+ - Which experts were marked RELEVANT
+ - Which specific files each expert identified as relevant to their domain
+ - The PR number and branch
+
+2. **Invoke relevant experts for deep review**
+
+ For each RELEVANT expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Instead of reading the diff yourself and passing it to each expert, use `$(command)`
+ syntax in the prompt. This embeds the command output directly when the sub-agent
+ is spawned, avoiding token overhead in the main conversation.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Perform a detailed code review of these PR changes **focusing specifically on your domain of expertise**.
+
+ IMPORTANT: Only comment on aspects that fall within your area of expertise. Do not provide
+ general code review feedback on things outside your domain - other experts will cover those areas.
+ Use your specialized knowledge to identify issues that a generalist reviewer might miss.
+
+ Your domain: [brief description from discovery_description]
+
+ Files you identified as relevant to your domain:
+ [list from relevance assessment]
+
+ ## PR Diff
+
+ $(gh pr diff)
+
+ ## Your Task
+
+ Review the diff above, focusing ONLY on files and changes relevant to your domain.
+ Ignore changes outside your expertise - other experts will cover those.
+
+ From your expert perspective, provide your review in this format:
+
+ ## Summary
+ Overall assessment of the changes as they relate to your domain (1-2 paragraphs).
+ What is this PR doing in terms of your area of expertise?
+
+ ## Issues Found
+ For each issue within your domain:
+ - **File**: path/to/file
+ - **Line(s)**: [line numbers]
+ - **Severity**: Critical / Major / Minor / Suggestion
+ - **Issue**: Description of the problem from your expert perspective
+ - **Suggestion**: How to fix it, drawing on your domain knowledge
+
+ ## Code Suggestions
+ Specific code changes you recommend based on your expertise (include before/after snippets).
+ Explain why this change is better from your domain's perspective.
+
+ ## Approval Status
+ - APPROVED: No blocking issues within your domain
+ - CHANGES_REQUESTED: Blocking issues in your domain must be addressed
+ - COMMENTS: Suggestions only within your domain, no blockers
+ ```
+
+ **Note**: Invoke relevant experts in parallel for efficiency.
+
+3. **Collect expert reviews**
+
+ Wait for all expert reviews to complete. Save each to their dedicated review file.
+
+4. **Create consolidated summary**
+
+ After all reviews complete, summarize across experts:
+ - Total issues by severity
+ - Key themes across reviews
+ - Overall approval status
+
+## Output Format
+
+### pr_review/[expert_name]/review.md
+
+Create one file per relevant expert:
+
+```markdown
+# [Expert Name] Review
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+**Reviewer**: [expert-name] expert
+
+## Summary
+
+[Expert's overall assessment from their domain perspective]
+
+## Issues Found
+
+### Issue 1
+- **File**: path/to/file.py
+- **Line(s)**: 45-52
+- **Severity**: Major
+- **Issue**: [Description from expert perspective]
+- **Suggestion**: [How to fix, using domain knowledge]
+
+### Issue 2
+...
+
+## Code Suggestions
+
+### Suggestion 1: [Brief title]
+
+**File**: path/to/file.py
+
+Before:
+```python
+# problematic code
+```
+
+After:
+```python
+# suggested improvement
+```
+
+**Rationale**: [Why this change improves the code from this expert's perspective]
+
+...
+
+## Approval Status
+
+[APPROVED / CHANGES_REQUESTED / COMMENTS]
+
+[Additional notes on approval status]
+```
+
+### pr_review/review_summary.md (optional but recommended)
+
+```markdown
+# PR Review Summary
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+
+## Expert Reviews
+
+| Expert | Domain | Status | Critical | Major | Minor |
+|--------|--------|--------|----------|-------|-------|
+| [name] | [domain] | CHANGES_REQUESTED | 0 | 2 | 3 |
+| [name] | [domain] | APPROVED | 0 | 0 | 1 |
+
+## Key Themes
+
+- [Theme 1 appearing across multiple reviews]
+- [Theme 2]
+
+## Overall Status
+
+[CHANGES_REQUESTED if any expert requested changes, otherwise APPROVED]
+
+## Next Steps
+
+[If changes requested]: Run `/experts.improve_and_rereview` to address feedback
+[If approved]: PR is ready to merge
+```
+
+## Quality Criteria
+
+- Only experts marked as relevant were invoked
+- Each expert produced written feedback in their review file
+- Feedback is focused on each expert's specific domain of expertise
+- Specific code change suggestions are included where applicable
+- Issues include file, line numbers, severity, and suggestions
+- Approval status is clearly stated by each expert
+
+## Context
+
+This is the second phase of the expert-driven PR review workflow. Deep review only happens after relevance filtering to ensure expert time is well-spent. Each expert reviews independently from their unique domain perspective, bringing specialized knowledge to identify issues that generalist reviewers might miss.
+
+The key to effective expert review is **focus**: each expert should only comment on aspects within their domain, trusting that other experts will cover other areas. This produces higher-quality, more actionable feedback than generic reviews.
+
+The review files serve as input to the `improve_and_rereview` step if changes are requested. If all experts approve, the PR can proceed to merge.
+
+
+### Workflow Context
+
+Orchestrate a comprehensive PR review using domain experts:
+1. Check which experts have relevant expertise for the PR changes
+2. Have relevant experts perform deep code review
+3. Iteratively improve the PR based on feedback until all experts approve
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `pr_review/relevance_assessments.md` (from `check_relevance`)
+
+## Work Branch
+
+Use branch format: `deepwork/experts-review_pr-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-review_pr-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/{expert_name}/review.md`
+- `pr_review/review_summary.md`
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "review_pr step 2/3 complete, outputs: pr_review/{expert_name}/review.md, pr_review/review_summary.md"
+3. **Continue workflow**: Use Skill tool to invoke `/experts.improve_and_rereview`
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/review_pr/workflow.yml`, `.deepwork/experts/experts/workflows/review_pr/steps/deep_review.md`
\ No newline at end of file
diff --git a/.claude/skills/experts.define/SKILL.md b/.claude/skills/experts.define/SKILL.md
new file mode 100644
index 00000000..974845de
--- /dev/null
+++ b/.claude/skills/experts.define/SKILL.md
@@ -0,0 +1,168 @@
+---
+name: experts.define
+description: "Create a workflow.yml by gathering requirements through structured questions"
+context: fork
+agent: experts
+
+---
+
+# experts.define
+
+**Step 1/3** in **new_workflow** workflow
+
+> Create a new multi-step DeepWork workflow with spec definition, review, and implementation
+
+
+## Instructions
+
+**Goal**: Create a workflow.yml by gathering requirements through structured questions
+
+# Define Workflow Specification
+
+## Objective
+
+Create a `workflow.yml` specification file that defines a new DeepWork workflow by understanding the user's requirements through interactive questioning.
+
+## Task
+
+Guide the user through defining a workflow specification by asking structured questions. The output is **only** the `workflow.yml` file - step instruction files are created in the `implement` step.
+
+### Phase 1: Understand the Workflow Purpose
+
+Ask structured questions to understand the workflow:
+
+1. **Overall goal** - What complex task are they trying to accomplish? What domain (research, marketing, development, reporting)?
+
+2. **Success criteria** - What's the final deliverable? Who is the audience? What quality matters most?
+
+3. **Major phases** - What are the distinct stages from start to finish? Any dependencies between phases?
+
+4. **Expert context** - Which expert should this workflow belong to? Existing one or create new?
+
+### Phase 2: Detect Document-Oriented Workflows
+
+Check for document-focused patterns in the user's description:
+- Keywords: "report", "summary", "document", "monthly", "quarterly", "for stakeholders"
+- Final deliverable is a specific document type
+- Recurring documents with consistent structure
+
+**If detected**, offer to create a doc spec:
+1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first."
+2. Ask if they want to create a doc spec, use existing one, or skip
+
+**If creating a doc spec**, gather:
+- Document name and purpose
+- Target audience and frequency
+- Quality criteria (3-5, focused on the output document itself)
+- Document structure (sections, required elements)
+
+Create at `.deepwork/doc_specs/[doc_spec_name].md`.
+
+### Phase 3: Define Each Step
+
+For each major phase, gather:
+
+1. **Purpose** - What does this step accomplish? What are inputs and outputs?
+
+2. **Inputs**:
+ - User-provided parameters (e.g., topic, target audience)?
+ - Files from previous steps?
+
+3. **Outputs**:
+ - What files does this step produce?
+ - Format (markdown, YAML, JSON)?
+ - Where to save? (Use meaningful paths like `competitive_research/analysis.md`)
+ - Does this output have a doc spec?
+
+4. **Dependencies** - Which previous steps must complete first?
+
+5. **Process** - Key activities? Quality checks needed?
+
+6. **Agent Delegation** - Should this step run via a specific expert? Use `agent: experts` for domain-specific expertise.
+
+#### Output Path Guidelines
+
+- Place outputs in main repo, not dot-directories
+- Use workflow name as top-level folder for workflow-specific outputs
+- Include dates for periodic outputs that accumulate (monthly reports)
+- Omit dates for current-state outputs that get updated in place
+- Use `_dataroom` folders for supporting materials
+
+### Phase 4: Validate the Workflow
+
+After gathering all step information:
+
+1. **Review the flow** - Summarize the complete workflow, show how outputs feed into next steps
+2. **Check for gaps** - Undefined inputs? Unused outputs? Circular dependencies?
+3. **Confirm details** - Workflow name (lowercase_underscores), summary (max 200 chars), description (detailed), version (1.0.0)
+4. **Step ID uniqueness** - Step IDs must be unique within the expert, across all workflows
+
+### Phase 5: Create the Workflow
+
+Determine the expert and create the directory structure:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/steps
+```
+
+Create `workflow.yml` at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+**Validation rules**:
+- Workflow name: lowercase, underscores, no spaces (must match folder name)
+- Version: semantic versioning (1.0.0)
+- Summary: under 200 characters
+- Step IDs: unique within the expert, lowercase with underscores
+- Dependencies must reference existing steps
+- File inputs with `from_step` must be in dependencies
+- At least one output per step
+
+## Output
+
+**File**: `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+
+After creating the file, the next step will review it against quality criteria.
+
+
+### Workflow Context
+
+Guide the user through creating a new DeepWork workflow by:
+1. Understanding their workflow requirements through interactive questioning
+2. Creating a validated workflow.yml specification
+3. Reviewing the spec against quality criteria
+4. Generating step instruction files and syncing skills
+
+
+## Required Inputs
+
+**User Parameters** - Gather from user before starting:
+- **workflow_purpose**: What complex task are you trying to accomplish?
+
+
+## Work Branch
+
+Use branch format: `deepwork/experts-new_workflow-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-new_workflow-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml`
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "new_workflow step 1/3 complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+3. **Continue workflow**: Use Skill tool to invoke `/experts.review_workflow_spec`
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/new_workflow/workflow.yml`, `.deepwork/experts/experts/workflows/new_workflow/steps/define.md`
\ No newline at end of file
diff --git a/.claude/skills/experts.implement/SKILL.md b/.claude/skills/experts.implement/SKILL.md
new file mode 100644
index 00000000..5138b784
--- /dev/null
+++ b/.claude/skills/experts.implement/SKILL.md
@@ -0,0 +1,167 @@
+---
+name: experts.implement
+description: "Generate step instruction files and sync skills from the validated workflow.yml"
+context: fork
+agent: experts
+
+---
+
+# experts.implement
+
+**Step 3/3** in **new_workflow** workflow
+
+> Create a new multi-step DeepWork workflow with spec definition, review, and implementation
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/experts.review_workflow_spec`
+
+## Instructions
+
+**Goal**: Generate step instruction files and sync skills from the validated workflow.yml
+
+# Implement Workflow Steps
+
+## Objective
+
+Generate the step instruction files for each step in the validated `workflow.yml` specification, then sync the skills.
+
+## Task
+
+Read the `workflow.yml` specification and create all necessary files to make the workflow functional, then sync the commands.
+
+### Step 1: Create Directory Structure
+
+Create the workflow directories:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/{steps,hooks}
+```
+
+This creates:
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/` - Main workflow directory
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/` - Step instruction files
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/hooks/` - Custom validation scripts
+
+**Note**: If directory exists from define step, skip or just create missing subdirectories.
+
+### Step 2: Read and Validate the Specification
+
+1. Read `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+2. Validate: name, version, summary, steps are present
+3. Check dependencies reference existing steps, no circular dependencies
+4. Verify file inputs match dependencies
+
+### Step 3: Generate Step Instruction Files
+
+For each step in workflow.yml, create `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`.
+
+**Guidelines**:
+
+1. **Use the workflow description** - It provides crucial context
+2. **Be specific** - Tailor instructions to the step's purpose, not generic
+3. **Provide examples** - Show what good output looks like
+4. **Explain the "why"** - Help understand the step's role in the workflow
+5. **Ask structured questions** - Steps with user inputs MUST explicitly tell the agent to "ask structured questions"
+6. **Align with hooks** - If step has hooks, ensure quality criteria match
+
+Each instruction file should include:
+- **Objective** - What this step accomplishes
+- **Task** - Detailed process
+- **Output Format** - Examples of expected outputs
+- **Quality Criteria** - How to verify completion
+
+### Step 4: Verify workflow.yml Location
+
+Ensure `workflow.yml` is at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+### Step 5: Sync Skills
+
+Run:
+
+```bash
+deepwork sync
+```
+
+This generates skills in `.claude/skills/` (or appropriate platform directory).
+
+### Step 6: Consider Rules
+
+After implementing, consider whether **rules** would help this workflow's domain.
+
+**What are rules?** Automated guardrails that trigger when certain files change, ensuring:
+- Documentation stays in sync
+- Team guidelines are followed
+- Quality standards are maintained
+
+**When to suggest rules:**
+- Does this workflow produce outputs that other files depend on?
+- Are there docs that should update when outputs change?
+- Could changes impact other parts of the project?
+
+**Examples**:
+| Workflow Type | Potential Rule |
+|---------------|----------------|
+| API Design | "Update API docs when endpoint definitions change" |
+| Competitive Research | "Update strategy docs when competitor analysis changes" |
+| Feature Development | "Update changelog when feature files change" |
+
+If you identify helpful rules, explain what they would do and offer: "Would you like me to create this rule? I can run `/deepwork-rules.define` to set it up."
+
+**Note**: Not every workflow needs rules. Only suggest when genuinely helpful.
+
+## Completion Checklist
+
+- [ ] workflow.yml in correct location
+- [ ] All step instruction files created (not stubs)
+- [ ] Instructions are specific and actionable
+- [ ] Output examples provided
+- [ ] Quality criteria defined for each step
+- [ ] `deepwork sync` executed successfully
+- [ ] Considered relevant rules for this workflow
+
+
+### Workflow Context
+
+Guide the user through creating a new DeepWork workflow by:
+1. Understanding their workflow requirements through interactive questioning
+2. Creating a validated workflow.yml specification
+3. Reviewing the spec against quality criteria
+4. Generating step instruction files and syncing skills
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml` (from `review_workflow_spec`)
+
+## Work Branch
+
+Use branch format: `deepwork/experts-new_workflow-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-new_workflow-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/` (directory)
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "new_workflow step 3/3 complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/"
+3. **new_workflow workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/new_workflow/workflow.yml`, `.deepwork/experts/experts/workflows/new_workflow/steps/implement.md`
\ No newline at end of file
diff --git a/.claude/skills/experts.improve_and_rereview/SKILL.md b/.claude/skills/experts.improve_and_rereview/SKILL.md
new file mode 100644
index 00000000..a3ac40bb
--- /dev/null
+++ b/.claude/skills/experts.improve_and_rereview/SKILL.md
@@ -0,0 +1,295 @@
+---
+name: experts.improve_and_rereview
+description: "Apply expert feedback and re-run reviews until no further feedback or 3 iterations"
+user-invocable: false
+context: fork
+agent: experts
+
+---
+
+# experts.improve_and_rereview
+
+**Step 3/3** in **review_pr** workflow
+
+> Coordinate expert-driven PR review with iterative improvement cycles
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/experts.deep_review`
+
+## Instructions
+
+**Goal**: Apply expert feedback and re-run reviews until no further feedback or 3 iterations
+
+# Improve and Re-Review
+
+## Objective
+
+Iteratively improve the PR based on expert feedback, then re-run expert reviews until all feedback is addressed or a maximum of 3 iterations is reached.
+
+## Task
+
+Apply improvements based on expert review feedback, get user approval, then have the same experts re-review. Repeat until experts report no further issues.
+
+### Process
+
+1. **Read expert reviews**
+
+ Read all review files from `pr_review/[expert_name]/review.md` to understand:
+ - What issues each expert identified within their domain
+ - What code changes they suggested
+ - Their approval status
+
+2. **Consolidate and prioritize feedback**
+
+ Group feedback by:
+ - **Critical/Major issues**: Must be addressed
+ - **Minor issues**: Should be addressed
+ - **Suggestions**: Nice to have
+
+ Present a summary to the user:
+ ```
+ ## Feedback Summary
+
+ ### Critical/Major Issues (must fix)
+ 1. [Issue from expert X]: [description]
+ 2. [Issue from expert Y]: [description]
+
+ ### Minor Issues (should fix)
+ 1. [Issue]: [description]
+
+ ### Suggestions (optional)
+ 1. [Suggestion]: [description]
+
+ ## Proposed Changes
+
+ I recommend making these changes:
+ 1. [Change 1]: [what and why]
+ 2. [Change 2]: [what and why]
+
+ Do you approve these changes?
+ ```
+
+3. **Get user approval**
+
+ **IMPORTANT**: Do not make changes without explicit user approval.
+
+ Wait for user to:
+ - Approve the proposed changes
+ - Modify which changes to apply
+ - Skip certain suggestions with justification
+
+4. **Apply approved changes**
+
+ Make the code changes the user approved:
+ - Use the Edit tool to modify files
+ - Follow the expert's code suggestions where applicable
+ - Ensure changes are coherent and don't break other code
+
+5. **Re-run expert reviews**
+
+ After applying changes, invoke the same relevant experts again:
+ - Use the Task tool with `subagent_type`: "experts"
+ - Provide the updated file contents and diff
+ - Ask them to re-review from their domain perspective
+
+ **Expert re-review prompt**:
+ ```
+ This is a re-review of PR changes after addressing your previous feedback.
+
+ Your domain: [brief description from discovery_description]
+
+ Previous issues you raised within your domain:
+ [list their issues from last review]
+
+ Changes made:
+ [summary of changes applied]
+
+ Updated files:
+ [full file contents]
+
+ From your expert perspective, please review and report:
+ 1. Are your previous domain-specific issues addressed?
+ 2. Any new issues introduced within your domain?
+ 3. Updated approval status for your domain
+ ```
+
+6. **Evaluate results**
+
+ After re-review:
+ - If ALL experts now approve (or only have minor suggestions): **Stop - review complete**
+ - If any expert still has Critical/Major issues: **Continue to next iteration**
+ - If this was iteration 3: **Stop - maximum iterations reached**
+
+7. **Create iteration summary**
+
+ Document what happened in this iteration.
+
+### Iteration Loop
+
+Repeat steps 2-7 until:
+- All experts report no further blocking feedback, OR
+- 3 iterations have been completed
+
+## Output Format
+
+### pr_review/iteration_[n]/summary.md
+
+Create one file per iteration:
+
+```markdown
+# Iteration [n] Summary
+
+**Date**: [YYYY-MM-DD]
+**Iteration**: [n] of 3 max
+
+## Feedback Addressed
+
+**IMPORTANT**: List ALL issues from expert reviews - none may be silently omitted.
+
+### From [expert-name]
+| Issue | Severity | Status | Resolution |
+|-------|----------|--------|------------|
+| [Issue 1] | Major | FIXED | [How it was fixed] |
+| [Issue 2] | Minor | SKIPPED | [Explicit reason: false positive / deferred / user declined] |
+| [Issue 3] | Suggestion | FIXED | [How it was fixed] |
+
+### From [expert-name-2]
+...
+
+## Changes Made
+
+1. **[file.py]**: [What was changed]
+2. **[other.ts]**: [What was changed]
+
+## Re-Review Results
+
+| Expert | Previous Status | New Status | Remaining Issues |
+|--------|-----------------|------------|------------------|
+| [name] | CHANGES_REQUESTED | APPROVED | 0 |
+| [name] | CHANGES_REQUESTED | COMMENTS | 1 minor |
+
+## Outcome
+
+[One of:]
+- **COMPLETE**: All experts approved. PR ready to merge.
+- **CONTINUING**: [n] blocking issues remain. Proceeding to iteration [n+1].
+- **MAX_ITERATIONS**: Reached 3 iterations. [n] issues remain unresolved.
+
+## Remaining Issues (if any)
+
+1. [Issue]: [Why not addressed / Plan for addressing]
+```
+
+### Final Summary (after last iteration)
+
+Update or create `pr_review/final_summary.md`:
+
+```markdown
+# PR Review Final Summary
+
+**PR**: #[number]
+**Total Iterations**: [n]
+**Final Status**: [APPROVED / PARTIAL / UNRESOLVED]
+
+## Review Timeline
+
+| Iteration | Date | Issues Addressed | Remaining |
+|-----------|------|------------------|-----------|
+| 1 | [date] | 5 | 3 |
+| 2 | [date] | 3 | 0 |
+
+## Expert Final Status
+
+| Expert | Final Status |
+|--------|--------------|
+| [name] | APPROVED |
+| [name] | APPROVED |
+
+## Key Improvements Made
+
+1. [Improvement 1]
+2. [Improvement 2]
+
+## Issue Resolution Summary
+
+| Expert | Total Issues | Fixed | Skipped (with reason) |
+|--------|--------------|-------|----------------------|
+| [name] | 4 | 3 | 1 (false positive) |
+| [name] | 2 | 2 | 0 |
+
+## Unresolved Items (if any)
+
+For each unresolved item, document:
+- Issue description
+- Why it wasn't fixed
+- Plan for addressing (if applicable)
+
+## Next Steps
+
+[What to do with the PR now]
+```
+
+## Quality Criteria
+
+- **Complete issue accounting**: Every issue from expert reviews is either:
+ - Fixed (with description of the fix), OR
+ - Explicitly skipped with documented justification (e.g., "false positive", "deferred to future PR", "user declined")
+- **No silent omissions**: The feedback summary must list ALL issues from expert reviews, not a subset
+- User approved suggested improvements before they were applied
+- Re-reviews were run after each improvement iteration
+- Cycle stopped when all experts reported no further feedback OR after 3 iterations
+- Final iteration summary documents the outcome for every issue
+- All changes are tracked and attributed to expert feedback
+
+## Context
+
+This is the final phase of the expert-driven PR review workflow. The iterative approach ensures feedback is actually addressed, not just acknowledged. The 3-iteration limit prevents infinite loops while allowing reasonable time for improvements.
+
+User approval at each step keeps humans in control - experts suggest, but humans decide what changes to make. This respects developer autonomy while benefiting from expert review.
+
+
+### Workflow Context
+
+Orchestrate a comprehensive PR review using domain experts:
+1. Check which experts have relevant expertise for the PR changes
+2. Have relevant experts perform deep code review
+3. Iteratively improve the PR based on feedback until all experts approve
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `pr_review/{expert_name}/review.md` (from `deep_review`)
+
+## Work Branch
+
+Use branch format: `deepwork/experts-review_pr-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-review_pr-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/iteration_{n}/summary.md`
+- `pr_review/final_summary.md`
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "review_pr step 3/3 complete, outputs: pr_review/iteration_{n}/summary.md, pr_review/final_summary.md"
+3. **review_pr workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/review_pr/workflow.yml`, `.deepwork/experts/experts/workflows/review_pr/steps/improve_and_rereview.md`
\ No newline at end of file
diff --git a/.claude/skills/experts.learn/SKILL.md b/.claude/skills/experts.learn/SKILL.md
new file mode 100644
index 00000000..ba588995
--- /dev/null
+++ b/.claude/skills/experts.learn/SKILL.md
@@ -0,0 +1,176 @@
+---
+name: experts.learn
+description: "Analyze conversation to extract learnings and improve instructions"
+context: fork
+agent: experts
+
+---
+
+# experts.learn
+
+**Step 1/1** in **learn** workflow
+
+> Analyze conversation history to improve workflow instructions and capture learnings
+
+
+## Instructions
+
+**Goal**: Analyze conversation to extract learnings and improve instructions
+
+# Learn from Workflow Execution
+
+## Objective
+
+Reflect on the conversation to identify learnings from DeepWork workflow executions, improve workflow instructions with generalizable insights, and capture run-specific learnings in AGENTS.md files.
+
+## Task
+
+Analyze conversation history to extract learnings, then apply them:
+- **Generalizable learnings** -> Update workflow instruction files
+- **Bespoke learnings** (run-specific) -> Add to AGENTS.md in the working folder
+
+### Step 1: Analyze Conversation for Workflow Executions
+
+1. **Scan the conversation** for DeepWork slash commands (`/expert-name.step_id`)
+2. **Identify the target folder** - the deepest common folder for all work on the topic
+3. **If no workflow specified**, ask which workflow to learn from
+
+### Step 2: Identify Confusion and Inefficiency
+
+Review the conversation for:
+
+**Confusion signals**:
+- Unnecessary questions the agent asked
+- Misunderstandings about step requirements
+- Incorrect outputs needing correction
+- Ambiguous instructions causing wrong interpretations
+
+**Inefficiency signals**:
+- Extra iterations needed
+- Repeated information
+- Missing context
+- Unclear dependencies
+
+**Error patterns**:
+- Failed validations and why
+- Misunderstood quality criteria
+- Unhandled edge cases
+
+**Success patterns**:
+- What worked well
+- Efficient approaches worth preserving
+- Good examples to add to instructions
+
+### Step 3: Classify Learnings
+
+For each learning, determine if it is:
+
+**Generalizable** (update instructions):
+- Would help ANY future run of this workflow
+- Addresses unclear or missing guidance
+- Fixes incorrect assumptions
+- Adds helpful examples
+
+**Doc spec-related** (update doc spec files):
+- Improvements to document quality criteria
+- Changes to document structure or format
+
+**Bespoke** (add to AGENTS.md):
+- Specific to THIS project/codebase/run
+- Depends on local conventions
+- References specific files or paths
+- Would not apply to other uses
+
+### Step 4: Update Workflow Instructions (Generalizable)
+
+For each generalizable learning:
+
+1. Locate `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`
+2. Make targeted improvements:
+ - Add missing context
+ - Include helpful examples
+ - Clarify ambiguous instructions
+ - Update quality criteria
+3. Keep instructions concise - avoid redundancy
+4. Preserve structure (Objective, Task, Output Format, Quality Criteria)
+
+### Step 5: Update Doc Specs (if applicable)
+
+If doc spec-related learnings identified:
+
+1. Locate doc spec at `.deepwork/doc_specs/[doc_spec_name].md`
+2. Update quality_criteria, example document, or metadata as needed
+
+### Step 6: Create/Update AGENTS.md (Bespoke)
+
+1. Place in the deepest common folder for the topic
+2. Use file references instead of duplicating content: "See `path/to/file.ext` for [description]"
+
+**Good patterns** (references):
+```markdown
+- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
+- Configuration schema: Defined in `config/schema.json`
+```
+
+**Avoid** (duplicating):
+```markdown
+- API endpoints should return JSON with this format: { status: ..., data: ... }
+```
+
+### Step 7: Update Workflow Version and Sync
+
+If instructions were modified:
+
+1. Bump version in workflow.yml (patch for improvements, minor for quality criteria changes)
+2. Add changelog entry
+3. Run `deepwork sync`
+
+## Output
+
+- Updated workflow instructions (generalizable learnings)
+- Updated doc specs (if applicable)
+- AGENTS.md with bespoke learnings in the correct working folder
+
+
+### Workflow Context
+
+Reflect on the conversation to identify learnings from DeepWork workflow executions,
+improve workflow instructions with generalizable insights, and capture run-specific
+learnings in AGENTS.md files.
+
+
+## Required Inputs
+
+**User Parameters** - Gather from user before starting:
+- **expert_name**: Name of the expert whose workflow to learn from (optional - will scan conversation if not provided)
+
+
+## Work Branch
+
+Use branch format: `deepwork/experts-learn-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-learn-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/` (directory)
+- `AGENTS.md`
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "learn step 1/1 complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/, AGENTS.md"
+3. **learn workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/learn/workflow.yml`, `.deepwork/experts/experts/workflows/learn/steps/learn.md`
\ No newline at end of file
diff --git a/.claude/skills/experts.new_workflow/SKILL.md b/.claude/skills/experts.new_workflow/SKILL.md
new file mode 100644
index 00000000..8183f2d5
--- /dev/null
+++ b/.claude/skills/experts.new_workflow/SKILL.md
@@ -0,0 +1,60 @@
+---
+name: experts.new_workflow
+description: "Create a new multi-step DeepWork workflow with spec definition, review, and implementation"
+---
+
+# experts.new_workflow
+
+Create a new multi-step DeepWork workflow with spec definition, review, and implementation
+
+> **CRITICAL**: Always invoke steps using the Skill tool. Never copy/paste step instructions directly.
+
+Guide the user through creating a new DeepWork workflow by:
+1. Understanding their workflow requirements through interactive questioning
+2. Creating a validated workflow.yml specification
+3. Reviewing the spec against quality criteria
+4. Generating step instruction files and syncing skills
+
+
+## Steps in Order
+
+1. **define** - Create a workflow.yml by gathering requirements through structured questions
+2. **review_workflow_spec** - Review workflow.yml against quality criteria using a sub-agent for unbiased validation
+3. **implement** - Generate step instruction files and sync skills from the validated workflow.yml
+
+**Start workflow**: `/experts.define`
+
+## Execution Instructions
+
+### Step 1: Analyze Intent
+
+Parse any text following `/experts.new_workflow` to determine user intent:
+- "define" or related terms → run step `experts.define`
+- "review_workflow_spec" or related terms → run step `experts.review_workflow_spec`
+- "implement" or related terms → run step `experts.implement`
+- No specific step mentioned → start at first step `experts.define`
+
+### Step 2: Invoke Starting Step
+
+Use the Skill tool to invoke the identified starting step:
+```
+Skill tool: experts.define
+```
+
+### Step 3: Continue Workflow Automatically
+
+After each step completes:
+1. Check if there's a next step in the workflow sequence
+2. Invoke the next step using the Skill tool
+3. Repeat until workflow is complete or user intervenes
+
+## Guardrails
+
+- Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
+- Do NOT skip steps in a workflow unless the user explicitly requests it
+- Do NOT proceed to the next step if the current step's outputs are incomplete
+- Do NOT make assumptions about user intent; ask for clarification when ambiguous
+
+## Context Files
+
+- Workflow definition: `.deepwork/experts/experts/workflows/new_workflow/workflow.yml`
\ No newline at end of file
diff --git a/.claude/skills/experts.review_pr/SKILL.md b/.claude/skills/experts.review_pr/SKILL.md
new file mode 100644
index 00000000..46921b9b
--- /dev/null
+++ b/.claude/skills/experts.review_pr/SKILL.md
@@ -0,0 +1,59 @@
+---
+name: experts.review_pr
+description: "Coordinate expert-driven PR review with iterative improvement cycles"
+---
+
+# experts.review_pr
+
+Coordinate expert-driven PR review with iterative improvement cycles
+
+> **CRITICAL**: Always invoke steps using the Skill tool. Never copy/paste step instructions directly.
+
+Orchestrate a comprehensive PR review using domain experts:
+1. Check which experts have relevant expertise for the PR changes
+2. Have relevant experts perform deep code review
+3. Iteratively improve the PR based on feedback until all experts approve
+
+
+## Steps in Order
+
+1. **check_relevance** - Invoke all experts in parallel to assess if PR changes are relevant to their domain
+2. **deep_review** - Invoke only relevant experts to perform detailed code review
+3. **improve_and_rereview** - Apply expert feedback and re-run reviews until no further feedback or 3 iterations
+
+**Start workflow**: `/experts.check_relevance`
+
+## Execution Instructions
+
+### Step 1: Analyze Intent
+
+Parse any text following `/experts.review_pr` to determine user intent:
+- "check_relevance" or related terms → run step `experts.check_relevance`
+- "deep_review" or related terms → run step `experts.deep_review`
+- "improve_and_rereview" or related terms → run step `experts.improve_and_rereview`
+- No specific step mentioned → start at first step `experts.check_relevance`
+
+### Step 2: Invoke Starting Step
+
+Use the Skill tool to invoke the identified starting step:
+```
+Skill tool: experts.check_relevance
+```
+
+### Step 3: Continue Workflow Automatically
+
+After each step completes:
+1. Check if there's a next step in the workflow sequence
+2. Invoke the next step using the Skill tool
+3. Repeat until workflow is complete or user intervenes
+
+## Guardrails
+
+- Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
+- Do NOT skip steps in a workflow unless the user explicitly requests it
+- Do NOT proceed to the next step if the current step's outputs are incomplete
+- Do NOT make assumptions about user intent; ask for clarification when ambiguous
+
+## Context Files
+
+- Workflow definition: `.deepwork/experts/experts/workflows/review_pr/workflow.yml`
\ No newline at end of file
diff --git a/.claude/skills/experts.review_workflow_spec/SKILL.md b/.claude/skills/experts.review_workflow_spec/SKILL.md
new file mode 100644
index 00000000..a88c64c2
--- /dev/null
+++ b/.claude/skills/experts.review_workflow_spec/SKILL.md
@@ -0,0 +1,164 @@
+---
+name: experts.review_workflow_spec
+description: "Review workflow.yml against quality criteria using a sub-agent for unbiased validation"
+user-invocable: false
+context: fork
+agent: experts
+
+---
+
+# experts.review_workflow_spec
+
+**Step 2/3** in **new_workflow** workflow
+
+> Create a new multi-step DeepWork workflow with spec definition, review, and implementation
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/experts.define`
+
+## Instructions
+
+**Goal**: Review workflow.yml against quality criteria using a sub-agent for unbiased validation
+
+# Review Workflow Specification
+
+## Objective
+
+Review the `workflow.yml` created in the define step against quality criteria, then iterate on fixes until all criteria pass.
+
+## Why This Step Exists
+
+The define step focuses on understanding user requirements. This review step ensures the specification meets quality standards before implementation. A fresh review catches issues that might be missed after being deeply involved in definition.
+
+## Task
+
+Review the workflow.yml against all quality criteria, fix any failures, and repeat until all pass.
+
+### Step 1: Read the Files
+
+Read the workflow specification:
+```
+.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml
+```
+
+### Step 2: Evaluate Against Quality Criteria
+
+Review the workflow.yml against these criteria:
+
+1. **Valid Identifier** - Workflow name is lowercase with underscores, matches folder name
+2. **Semantic Version** - Version follows X.Y.Z format (e.g., `1.0.0`)
+3. **Concise Summary** - Summary is under 200 characters and clearly describes the workflow
+4. **Rich Description** - Description is multi-line and explains: problem solved, process, expected outcomes, target users
+5. **Complete Steps** - Each step has: id, name, description, instructions_file, outputs
+6. **Unique Step IDs** - Step IDs are unique within the expert (across all workflows)
+7. **Valid Dependencies** - Dependencies reference existing step IDs with no circular references
+8. **Input Consistency** - File inputs with `from_step` reference a step in the dependencies array
+9. **Output Paths** - Outputs are valid filenames or paths
+
+For each criterion, determine PASS or FAIL. If FAIL, note the specific issue and fix.
+
+### Step 3: Fix Failed Criteria
+
+For each failed criterion, edit the workflow.yml:
+
+| Criterion | Common Issue | Fix |
+|-----------|-------------|-----|
+| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
+| Semantic Version | Invalid format | Set to `"1.0.0"` |
+| Concise Summary | Too long | Shorten to <200 chars |
+| Rich Description | Single line | Add multi-line explanation |
+| Complete Steps | Missing fields | Add required fields |
+| Unique Step IDs | Duplicate ID | Rename to unique identifier |
+| Valid Dependencies | Non-existent step | Fix step ID reference |
+| Input Consistency | from_step not in deps | Add step to dependencies |
+| Output Paths | Invalid format | Use valid filename/path |
+
+### Step 4: Re-Evaluate (If Needed)
+
+If any criteria failed:
+1. Review the updated workflow.yml
+2. Re-evaluate all criteria
+3. Fix remaining issues
+4. Repeat until all pass
+
+### Step 5: Confirm Completion
+
+When all criteria pass:
+
+1. Announce: "All workflow spec quality criteria pass."
+2. List what was validated
+3. Guide to next step: "Run `/experts.implement` to generate the step instruction files."
+
+## Output
+
+The validated `workflow.yml` file at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml` that passes all quality criteria.
+
+
+### Workflow Context
+
+Guide the user through creating a new DeepWork workflow by:
+1. Understanding their workflow requirements through interactive questioning
+2. Creating a validated workflow.yml specification
+3. Reviewing the spec against quality criteria
+4. Generating step instruction files and syncing skills
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml` (from `define`)
+
+## Work Branch
+
+Use branch format: `deepwork/experts-new_workflow-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/experts-new_workflow-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml`
+
+## Guardrails
+
+- Do NOT skip prerequisite verification if this step has dependencies
+- Do NOT produce partial outputs; complete all required outputs before finishing
+- Do NOT proceed without required inputs; ask the user if any are missing
+- Do NOT modify files outside the scope of this step's defined outputs
+
+## Quality Validation
+
+**Before completing this step, you MUST have your work reviewed against the quality criteria below.**
+
+Use a sub-agent (Haiku model) to review your work against these criteria:
+
+**Criteria (all must be satisfied)**:
+1. Workflow name is lowercase with underscores, no spaces or special characters
+2. Version follows X.Y.Z format (e.g., 1.0.0)
+3. Summary is under 200 characters and clearly describes the workflow
+4. Description is multi-line and explains problem, process, outcomes, and users
+5. Each step has id, name, description, instructions_file, outputs
+6. Step IDs are unique within the expert (across all workflows)
+7. Dependencies reference existing step IDs with no circular references
+8. File inputs with from_step reference a step in the dependencies array
+9. Outputs are valid filenames or paths
+**Review Process**:
+1. Once you believe your work is complete, spawn a sub-agent using Haiku to review your work against the quality criteria above
+2. The sub-agent should examine your outputs and verify each criterion is met
+3. If the sub-agent identifies valid issues, fix them
+4. Have the sub-agent review again until all valid feedback has been addressed
+5. Only mark the step complete when the sub-agent confirms all criteria are satisfied
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "new_workflow step 2/3 complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+3. **Continue workflow**: Use Skill tool to invoke `/experts.implement`
+
+---
+
+**Reference files**: `.deepwork/experts/experts/workflows/new_workflow/workflow.yml`, `.deepwork/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md`
\ No newline at end of file
diff --git a/.claude/skills/manual_tests/SKILL.md b/.claude/skills/manual_tests/SKILL.md
index 59b53adc..5806bdad 100644
--- a/.claude/skills/manual_tests/SKILL.md
+++ b/.claude/skills/manual_tests/SKILL.md
@@ -86,7 +86,7 @@ After each step completes:
### Handling Ambiguous Intent
-If user intent is unclear, use AskUserQuestion to clarify:
+If user intent is unclear, ask which option they want:
- Present available workflows and standalone skills as options
- Let user select the starting point
diff --git a/.claude/skills/update/SKILL.md b/.claude/skills/update/SKILL.md
index e63bd476..8571c169 100644
--- a/.claude/skills/update/SKILL.md
+++ b/.claude/skills/update/SKILL.md
@@ -57,7 +57,7 @@ After each step completes:
### Handling Ambiguous Intent
-If user intent is unclear, use AskUserQuestion to clarify:
+If user intent is unclear, ask which option they want:
- Present available steps as numbered options
- Let user select the starting point
diff --git a/.deepwork/experts/deepwork_rules/expert.yml b/.deepwork/experts/deepwork_rules/expert.yml
new file mode 100644
index 00000000..ea019c10
--- /dev/null
+++ b/.deepwork/experts/deepwork_rules/expert.yml
@@ -0,0 +1,89 @@
+discovery_description: |
+ DeepWork rules system - creating, organizing, and managing file-change rules that automatically trigger when specific files change during AI agent sessions.
+
+full_expertise: |
+ # DeepWork Rules Expert
+
+ Expert in creating and managing DeepWork rules - automated guardrails that trigger when specific files change during AI agent sessions.
+
+ ## What are Rules?
+
+ Rules are markdown files with YAML frontmatter that define:
+ - **Triggers**: File patterns that activate the rule when changed
+ - **Safety conditions**: File patterns that prevent the rule from firing when also changed
+ - **Instructions**: What the agent should do when the rule fires
+
+ ## Detection Modes
+
+ ### Trigger/Safety Mode (most common)
+ - Fires when trigger patterns match AND no safety patterns match
+ - Use for: "When X changes, check Y" rules
+ - Example: When config changes, verify install docs
+
+ ### Set Mode (bidirectional correspondence)
+ - Fires when files that should change together don't all change
+ - Use for: Source/test pairing, model/migration sync
+ - Example: `src/foo.py` and `tests/foo_test.py` should change together
+
+ ### Pair Mode (directional correspondence)
+ - Fires when a trigger file changes but expected files don't
+ - Changes to expected files alone do NOT trigger
+ - Use for: API code requires documentation updates (but docs can update independently)
+
+ ## Rule File Format
+
+ Rules live in `.deepwork/rules/` as markdown files:
+
+ ```markdown
+ ---
+ name: Rule Name
+ trigger: "pattern/**/*"
+ safety: "optional/pattern"
+ compare_to: base # optional
+ ---
+ Instructions for when this rule fires.
+ ```
+
+ ## Pattern Syntax
+
+ - `*` - Matches any characters within a single path segment
+ - `**` - Matches any characters across multiple path segments (recursive)
+ - `{name}` - Captures a single segment variable
+ - `{path}` - Captures multiple segments variable
+
+ ## Comparison Modes
+
+ - `base` (default) - Compare to merge-base with main/master
+ - `default_tip` - Compare to current tip of default branch
+ - `prompt` - Compare to state at start of each prompt
+
+ ## Action Types
+
+ Rules support two action types:
+
+ - **prompt** (default): Show instructions to the agent when the rule fires
+ - **command**: Execute a shell command automatically
+
+ Command action format:
+ ```markdown
+ ---
+ name: Auto Sync Dependencies
+ trigger: pyproject.toml
+ action: command
+ command: uv sync
+ ---
+ Dependencies will be automatically synced when pyproject.toml changes.
+ ```
+
+ Command actions are useful for:
+ - Auto-running `uv sync` when pyproject.toml changes
+ - Running linters when source files change
+ - Generating documentation when schemas change
+
+ ## When to Use Rules
+
+ Rules are most valuable for:
+ - Keeping documentation in sync with code
+ - Enforcing security reviews for sensitive changes
+ - Ensuring test coverage follows source changes
+ - Maintaining team guidelines automatically
diff --git a/.deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh b/.deepwork/experts/deepwork_rules/hooks/capture_prompt_work_tree.sh
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh
rename to .deepwork/experts/deepwork_rules/hooks/capture_prompt_work_tree.sh
diff --git a/.deepwork/jobs/deepwork_rules/hooks/global_hooks.yml b/.deepwork/experts/deepwork_rules/hooks/global_hooks.yml
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/hooks/global_hooks.yml
rename to .deepwork/experts/deepwork_rules/hooks/global_hooks.yml
diff --git a/.deepwork/jobs/deepwork_rules/hooks/user_prompt_submit.sh b/.deepwork/experts/deepwork_rules/hooks/user_prompt_submit.sh
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/hooks/user_prompt_submit.sh
rename to .deepwork/experts/deepwork_rules/hooks/user_prompt_submit.sh
diff --git a/.deepwork/jobs/deepwork_rules/rules/api-documentation-sync.md.example b/.deepwork/experts/deepwork_rules/rules/api-documentation-sync.md.example
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/rules/api-documentation-sync.md.example
rename to .deepwork/experts/deepwork_rules/rules/api-documentation-sync.md.example
diff --git a/.deepwork/jobs/deepwork_rules/rules/readme-documentation.md.example b/.deepwork/experts/deepwork_rules/rules/readme-documentation.md.example
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/rules/readme-documentation.md.example
rename to .deepwork/experts/deepwork_rules/rules/readme-documentation.md.example
diff --git a/.deepwork/jobs/deepwork_rules/rules/security-review.md.example b/.deepwork/experts/deepwork_rules/rules/security-review.md.example
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/rules/security-review.md.example
rename to .deepwork/experts/deepwork_rules/rules/security-review.md.example
diff --git a/.deepwork/jobs/deepwork_rules/rules/skill-md-validation.md b/.deepwork/experts/deepwork_rules/rules/skill-md-validation.md
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/rules/skill-md-validation.md
rename to .deepwork/experts/deepwork_rules/rules/skill-md-validation.md
diff --git a/.deepwork/jobs/deepwork_rules/rules/source-test-pairing.md.example b/.deepwork/experts/deepwork_rules/rules/source-test-pairing.md.example
similarity index 100%
rename from .deepwork/jobs/deepwork_rules/rules/source-test-pairing.md.example
rename to .deepwork/experts/deepwork_rules/rules/source-test-pairing.md.example
diff --git a/.deepwork/jobs/deepwork_rules/steps/define.md b/.deepwork/experts/deepwork_rules/workflows/define/steps/define.md
similarity index 90%
rename from .deepwork/jobs/deepwork_rules/steps/define.md
rename to .deepwork/experts/deepwork_rules/workflows/define/steps/define.md
index 1e38a5e6..b9f2416b 100644
--- a/.deepwork/jobs/deepwork_rules/steps/define.md
+++ b/.deepwork/experts/deepwork_rules/workflows/define/steps/define.md
@@ -8,7 +8,7 @@ Create a new rule file in the `.deepwork/rules/` directory to enforce team guide
Guide the user through defining a new rule by asking structured questions. **Do not create the rule without first understanding what they want to enforce.**
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
### Step 1: Understand the Rule Purpose
@@ -141,6 +141,22 @@ Changed API: {trigger_files}
Update docs: {expected_files}
```
+**Format for Command Action (auto-run commands):**
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies will be automatically synced when pyproject.toml changes.
+```
+
+Command actions execute automatically instead of prompting the agent. Use for:
+- Dependency syncing (`uv sync`, `npm install`)
+- Linting/formatting (`ruff format`, `prettier`)
+- Code generation tasks
+
### Step 7: Verify the Rule
After creating the rule:
@@ -223,6 +239,18 @@ Changed API: {trigger_files}
Update: {expected_files}
```
+### Auto-Sync Dependencies (Command Action)
+`.deepwork/rules/auto-sync-deps.md`:
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies are automatically synced when pyproject.toml changes.
+```
+
## Output Format
### .deepwork/rules/{rule-name}.md
diff --git a/.deepwork/experts/deepwork_rules/workflows/define/workflow.yml b/.deepwork/experts/deepwork_rules/workflows/define/workflow.yml
new file mode 100644
index 00000000..d77f3bcf
--- /dev/null
+++ b/.deepwork/experts/deepwork_rules/workflows/define/workflow.yml
@@ -0,0 +1,20 @@
+name: define
+version: "1.0.0"
+summary: "Create a new rule file that triggers when specified files change"
+
+description: |
+ Guide the user through defining a new DeepWork rule by asking structured questions
+ about what they want to enforce, then creating the rule file with proper YAML
+ frontmatter and markdown instructions.
+
+steps:
+ - id: define
+ name: "Define Rule"
+ description: "Create a new rule file that triggers when specified files change"
+ instructions_file: steps/define.md
+ inputs:
+ - name: rule_purpose
+ description: "What guideline or constraint should this rule enforce?"
+ outputs:
+ - ".deepwork/rules/{rule-name}.md"
+ exposed: true
diff --git a/.deepwork/experts/experts/expert.yml b/.deepwork/experts/experts/expert.yml
new file mode 100644
index 00000000..527cf68e
--- /dev/null
+++ b/.deepwork/experts/experts/expert.yml
@@ -0,0 +1,340 @@
+discovery_description: |
+ DeepWork experts system - creating domain knowledge collections with topics, learnings,
+ and multi-step workflows. Covers expert.yml, workflow.yml schema, skill generation, and CLI commands.
+
+full_expertise: |
+ # DeepWork Experts System
+
+ You are an expert on the DeepWork experts system - the framework for building
+ auto-improving collections of domain knowledge with embedded workflows.
+
+ ## Core Concepts
+
+ **Experts** are structured knowledge repositories that grow smarter over time.
+ Each expert represents deep knowledge in a specific domain and consists of:
+
+ - **Core expertise**: Foundational knowledge captured in expert.yml
+ - **Topics**: Detailed documentation on specific subjects
+ - **Learnings**: Hard-fought insights from real experiences
+ - **Workflows**: Multi-step task sequences that can be invoked as skills
+
+ ## Expert Structure
+
+ Experts live in `.deepwork/experts/[folder-name]/`:
+
+ ```
+ .deepwork/experts/
+ └── rails_activejob/
+ ├── expert.yml
+ ├── topics/
+ │ └── retry_handling.md
+ ├── learnings/
+ │ └── job_errors_not_going_to_sentry.md
+ └── workflows/
+ └── debug_jobs/
+ ├── workflow.yml
+ └── steps/
+ └── analyze.md
+ ```
+
+ The **expert name** derives from the folder name with spaces/underscores becoming
+ dashes: `rails_activejob` → `rails-activejob`.
+
+ ## Writing Good expert.yml
+
+ The expert.yml has two key fields:
+
+ ### discovery_description
+ A concise description (1-3 sentences) that helps the system decide when to
+ invoke this expert. Be specific about the domain and capabilities.
+
+ Good: "Ruby on Rails ActiveJob - background job processing, retries, queues,
+ and error handling in Rails applications."
+
+ Bad: "Helps with Rails stuff."
+
+ ### full_expertise
+ The core knowledge payload (~5 pages max). Structure it as:
+
+ 1. **Identity statement**: "You are an expert on..."
+ 2. **Core concepts**: Key ideas and mental models
+ 3. **Common patterns**: Typical approaches and solutions
+ 4. **Pitfalls to avoid**: Known gotchas and mistakes
+ 5. **Decision frameworks**: How to choose between options
+
+ Write in second person ("You should...") as this becomes agent instructions.
+
+ ## Writing Good Topics
+
+ Topics are deep dives into specific subjects within the domain.
+
+ ### When to create a topic
+ - Subject needs more detail than fits in full_expertise
+ - You find yourself repeatedly explaining something
+ - A subject has enough nuance to warrant dedicated documentation
+
+ ### Topic file structure
+ ```markdown
+ ---
+ name: Retry Handling
+ keywords:
+ - retry
+ - exponential backoff
+ - dead letter queue
+ last_updated: 2025-01-15
+ ---
+
+ [Detailed content here]
+ ```
+
+ ### Keyword guidelines
+ - Use topic-specific terms only
+ - Avoid broad domain terms (don't use "Rails" in a Rails expert's topics)
+ - Include synonyms and related terms users might search for
+ - 3-7 keywords is typical
+
+ ## Writing Good Learnings
+
+ Learnings capture hard-fought insights from real experiences - like mini
+ retrospectives that prevent repeating mistakes.
+
+ ### When to create a learning
+ - You solved a non-obvious problem
+ - A debugging session revealed unexpected behavior
+ - You discovered something that contradicts common assumptions
+ - Future-you would benefit from this context
+
+ ### Learning file structure
+ ```markdown
+ ---
+ name: Job errors not going to Sentry
+ last_updated: 2025-01-20
+ summarized_result: |
+ Sentry changed their standard gem for hooking into jobs.
+ SolidQueue still worked but ActiveJobKubernetes did not.
+ ---
+
+ ## Context
+ What was happening and why it mattered...
+
+ ## Investigation
+ What you tried and what you discovered...
+
+ ## Resolution
+ How you fixed it and why that worked...
+
+ ## Key Takeaway
+ The generalizable insight for future reference...
+ ```
+
+ ## Workflows
+
+ **Workflows** are multi-step task sequences embedded within experts. Each workflow
+ becomes a set of slash commands that users can invoke.
+
+ ### Workflow Structure
+
+ Workflows live in `workflows/[workflow_name]/`:
+
+ ```
+ workflows/
+ └── new_job/
+ ├── workflow.yml
+ └── steps/
+ ├── define.md
+ └── implement.md
+ ```
+
+ ### The workflow.yml Schema
+
+ Required fields:
+ - `name`: lowercase with underscores, must match folder name
+ - `version`: semantic versioning X.Y.Z (e.g., `1.0.0`)
+ - `summary`: concise description under 200 characters
+ - `steps`: array of step definitions
+
+ Optional fields:
+ - `description`: detailed multi-line explanation
+ - `execution_order`: explicit step ordering with concurrent support
+ - `changelog`: version history
+
+ ### Step Definition Fields
+
+ Each step requires:
+ - `id`: unique identifier within the expert (across all workflows)
+ - `name`: human-readable name
+ - `description`: what this step accomplishes
+ - `instructions_file`: path to step markdown (e.g., `steps/define.md`)
+ - `outputs`: array of output files
+
+ Optional step fields:
+ - `inputs`: user parameters or file inputs from previous steps
+ - `dependencies`: array of step IDs this step requires
+ - `exposed`: boolean, if true skill appears in user menus (default: false)
+ - `quality_criteria`: array of criteria strings for validation
+ - `agent`: agent type for delegation (e.g., `general-purpose`)
+ - `hooks`: lifecycle hooks for validation
+
+ ### Input Types
+
+ **User inputs** - parameters gathered from the user:
+ ```yaml
+ inputs:
+ - name: market_segment
+ description: "Target market segment for research"
+ ```
+
+ **File inputs** - outputs from previous steps:
+ ```yaml
+ inputs:
+ - file: competitors_list.md
+ from_step: identify_competitors
+ ```
+
+ ### Concurrent Execution
+
+ Steps can run in parallel using execution_order:
+ ```yaml
+ execution_order:
+ - define
+ - [research_a, research_b] # concurrent
+ - synthesize
+ ```
+
+ ### Lifecycle Hooks
+
+ Hooks trigger at specific points:
+ - `after_agent`: runs after agent finishes (quality validation)
+ - `before_tool`: runs before tool use
+ - `before_prompt`: runs when user submits prompt
+
+ Hook action types:
+ ```yaml
+ hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+ ```
+
+ Note: Claude Code currently only supports script hooks.
+
+ ## Skill Generation
+
+ Running `deepwork sync` generates skills from expert definitions:
+
+ 1. Parses all experts in `.deepwork/experts/`
+ 2. For each workflow, generates step skills and a workflow meta-skill
+ 3. Writes to platform directories (e.g., `.claude/skills/`)
+
+ **Two types of skills are generated:**
+
+ 1. **Step skills** - One per step, contains the actual instructions
+ - Naming: `{expert-name}.{step-id}`
+ - Example: `experts.define`, `rails-activejob.analyze`
+ - Path: `.claude/skills/[expert-name].[step-id]/SKILL.md`
+
+ 2. **Workflow meta-skills** - One per workflow, orchestrates the steps
+ - Naming: `{expert-name}.{workflow-name}`
+ - Example: `experts.new_workflow`, `experts.review_pr`
+ - Path: `.claude/skills/[expert-name].[workflow-name]/SKILL.md`
+ - Purpose: Entry point that routes to the first step and auto-continues through the workflow
+
+ ## CLI Commands
+
+ **Install DeepWork**:
+ ```bash
+ deepwork install --platform claude
+ ```
+ Creates `.deepwork/` structure, copies standard experts, runs sync.
+
+ **Sync skills**:
+ ```bash
+ deepwork sync
+ ```
+ Regenerates all skills from expert definitions.
+
+ **List topics**:
+ ```bash
+ deepwork topics --expert "expert-name"
+ ```
+
+ **List learnings**:
+ ```bash
+ deepwork learnings --expert "expert-name"
+ ```
+
+ ## How Experts Become Agents
+
+ Running `deepwork sync` generates Claude agents in `.claude/agents/`:
+
+ - Filename: `dwe_[expert-name].md`
+ - Agent name: `[expert-name]`
+ - Body: full_expertise + dynamic topic/learning lists
+
+ The dynamic embedding ensures agents always access current topics and learnings:
+ ```
+ $(deepwork topics --expert "expert-name")
+ $(deepwork learnings --expert "expert-name")
+ ```
+
+ ## Standard Experts
+
+ DeepWork ships with standard experts that are auto-installed:
+
+ - `experts`: This expert - creating and managing experts with workflows
+ - `new_job` workflow: Define, review, and implement new workflows
+ - `learn` workflow: Extract learnings from workflow executions
+ - `review_pr` workflow: Expert-driven PR review
+
+ - `deepwork_rules`: Create file-change trigger rules
+ - `define` workflow: Interactive rule creation
+
+ ## Writing Step Instructions
+
+ Step instruction files should include:
+
+ 1. **Objective**: Clear statement of what this step accomplishes
+ 2. **Task**: Detailed process with numbered steps
+ 3. **Inputs section**: What to gather/read before starting
+ 4. **Output format**: Examples of expected outputs
+ 5. **Quality criteria**: How to verify the step is complete
+
+ Use the phrase "ask structured questions" when gathering user input -
+ this triggers proper tooling for interactive prompts.
+
+ ## Common Patterns
+
+ **Creating a new workflow**:
+ 1. Run `/experts.define` (or just `/experts`)
+ 2. Answer structured questions about your workflow
+ 3. Review generated workflow.yml
+ 4. Run `/experts.implement` to generate step files
+ 5. Run `deepwork sync` to create skills
+
+ **Adding a step to existing workflow**:
+ 1. Edit `.deepwork/experts/[expert]/workflows/[workflow]/workflow.yml`
+ 2. Add step definition with required fields
+ 3. Create instructions file in `steps/`
+ 4. Run `deepwork sync`
+
+ ## Evolution Strategy
+
+ Experts should evolve through use:
+
+ 1. **Start minimal**: Begin with core expertise, add topics/learnings as needed
+ 2. **Capture immediately**: Document learnings right after solving problems
+ 3. **Refine periodically**: Review and consolidate as patterns emerge
+ 4. **Prune actively**: Remove outdated content, merge redundant topics
+
+ ## Naming Conventions
+
+ ### Expert folders
+ - Use lowercase with underscores: `rails_activejob`, `social_marketing`
+ - Be specific enough to be useful: `react_hooks` not just `react`
+
+ ### Workflow folders
+ - Use lowercase with underscores: `new_job`, `review_pr`
+ - Name describes the workflow's purpose
+
+ ### Step IDs
+ - Must be unique within the expert (across all workflows)
+ - Use lowercase with underscores: `define`, `implement`
diff --git a/.deepwork/experts/experts/learnings/.gitkeep b/.deepwork/experts/experts/learnings/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/.deepwork/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md b/.deepwork/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md
new file mode 100644
index 00000000..fc321212
--- /dev/null
+++ b/.deepwork/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md
@@ -0,0 +1,42 @@
+---
+name: Expert prompts must emphasize domain focus
+last_updated: 2026-02-01
+summarized_result: |
+ When invoking experts for tasks like PR review, prompts must explicitly tell
+ experts to ONLY comment on their domain and not provide generic feedback.
+ Without this, experts provide overlapping generic reviews instead of unique
+ specialized perspectives.
+---
+
+## Context
+
+During a PR review using the `/review_pr` job, both the `deepwork-jobs` and `experts` experts were invoked to review changes to the experts system implementation. The expectation was that each expert would bring their unique domain knowledge to identify issues that generalist reviewers might miss.
+
+## Problem
+
+Both experts provided similar generic code review feedback. They identified the same issues (redundant exception handling, unused variables) rather than focusing on aspects specific to their domains:
+
+- The `deepwork-jobs` expert should have focused on how the experts system integrates with jobs, skill generation patterns, and hook systems
+- The `experts` expert should have focused on expert.yml schema design, topic/learning structure, and evolution strategies
+
+Instead, both provided overlapping feedback on general code quality issues.
+
+## Resolution
+
+Updated the expert prompts in the `review_pr` job steps to explicitly emphasize domain focus:
+
+```
+IMPORTANT: Only comment on aspects that fall within your area of expertise.
+Do not provide general code review feedback on things outside your domain -
+other experts will cover those areas. Use your specialized knowledge to
+identify issues that a generalist reviewer might miss.
+```
+
+Also added:
+- "Your domain: [brief description from discovery_description]" to remind experts of their focus
+- Request for feedback "from your expert perspective" throughout the prompt
+- Quality criteria including "Feedback is focused on each expert's specific domain of expertise"
+
+## Key Takeaway
+
+When designing job steps that invoke experts, the prompt must explicitly constrain experts to their domain. Without this, experts default to providing generic assistance rather than specialized insight. The value of the experts system comes from each expert contributing unique perspective - overlapping generic reviews wastes the multi-expert approach.
diff --git a/.deepwork/experts/experts/learnings/keep_experts_focused.md b/.deepwork/experts/experts/learnings/keep_experts_focused.md
new file mode 100644
index 00000000..b77186a0
--- /dev/null
+++ b/.deepwork/experts/experts/learnings/keep_experts_focused.md
@@ -0,0 +1,33 @@
+---
+name: Keep Experts Focused
+last_updated: 2025-01-30
+summarized_result: |
+ Broad experts like "Rails" or "JavaScript" become too large and unfocused.
+ Better to create specific experts like "Rails ActiveJob" or "React Hooks".
+---
+
+## Context
+
+When initially designing the experts system, we considered creating broad,
+comprehensive experts that would cover entire technology stacks.
+
+## Investigation
+
+Testing showed that broad experts:
+- Generated overwhelming amounts of content
+- Struggled to provide specific, actionable guidance
+- Made it difficult to know which expert to invoke
+- Led to duplication across multiple broad experts
+
+## Resolution
+
+Adopted a principle of focused experts with clear boundaries:
+- Each expert covers a specific domain or technology subset
+- The discovery_description clearly indicates scope
+- Topics dive deep rather than wide
+- Learnings capture domain-specific insights
+
+## Key Takeaway
+
+An expert should be narrow enough that you can articulate its scope in 1-2
+sentences. If you can't, it's probably too broad.
diff --git a/.deepwork/experts/experts/learnings/prompt_hooks_not_executed.md b/.deepwork/experts/experts/learnings/prompt_hooks_not_executed.md
new file mode 100644
index 00000000..43ae0931
--- /dev/null
+++ b/.deepwork/experts/experts/learnings/prompt_hooks_not_executed.md
@@ -0,0 +1,50 @@
+---
+name: Prompt Hooks Not Executed in Claude Code
+last_updated: 2025-01-30
+summarized_result: |
+ Claude Code parses but does not execute prompt-based stop hooks.
+ Only script/command hooks actually run. Use quality_criteria for validation.
+---
+
+## Context
+
+When implementing quality validation for job steps, developers often try
+to use inline `prompt` or `prompt_file` hooks for validation.
+
+## Investigation
+
+Testing revealed that Claude Code's hook system only executes `command` type
+hooks in the settings.json hooks configuration. Prompt-based hooks are parsed
+by DeepWork but not rendered into the skill's hook frontmatter because they
+would not be executed.
+
+The template code explicitly filters:
+```jinja
+{%- set script_hooks = event_hooks | selectattr("type", "equalto", "script") | list %}
+```
+
+## Resolution
+
+Two recommended approaches for quality validation:
+
+1. **Use `quality_criteria` field** (preferred):
+ ```yaml
+ quality_criteria:
+ - "Each competitor has description"
+ - "Sources are cited"
+ ```
+ This generates instructions for sub-agent review, which works reliably.
+
+2. **Use script hooks** for objective validation:
+ ```yaml
+ hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+ ```
+ Scripts actually execute and can fail the step.
+
+## Key Takeaway
+
+For subjective quality checks, use the `quality_criteria` field which triggers
+sub-agent review. For objective checks (tests, linting), use script hooks.
+Avoid prompt hooks until Claude Code supports them.
diff --git a/.deepwork/experts/experts/learnings/review_pr_efficiency_improvements.md b/.deepwork/experts/experts/learnings/review_pr_efficiency_improvements.md
new file mode 100644
index 00000000..8ede78c5
--- /dev/null
+++ b/.deepwork/experts/experts/learnings/review_pr_efficiency_improvements.md
@@ -0,0 +1,74 @@
+---
+name: Review PR job efficiency improvements
+last_updated: 2026-02-01
+summarized_result: |
+ The review_pr job used excessive tokens by sending full file contents to all
+ experts instead of using the per-expert file segmentation from the relevance
+ step. Also, quality validation sub-agents add overhead that may not be needed
+ for simple checklist verification.
+---
+
+## Context
+
+During execution of the `/review_pr` job on PR #192, the workflow completed successfully but used significantly more tokens and time than necessary.
+
+## Problems Identified
+
+### 1. Ignored per-expert file segmentation
+
+The `check_relevance` step correctly identified which files each expert should review:
+- deepwork-jobs: schema, parser, generator, CLI, templates, tests
+- experts: same files (significant overlap in this case)
+
+But the `deep_review` step ignored this segmentation and sent ALL files to BOTH experts. Each expert received ~1000+ lines of code when they should have received only their relevant subset.
+
+### 2. Full file contents vs. targeted excerpts
+
+Experts received full file contents when often they only needed:
+- The diff (what changed)
+- Specific sections relevant to their domain
+- Perhaps function signatures for context
+
+Sending full test files (500+ lines) to review production code changes is wasteful.
+
+### 3. Quality validation sub-agent overhead
+
+Each step spawned a Haiku sub-agent purely to verify a checklist of 3-5 criteria. This adds:
+- Network round-trip latency
+- Token overhead for the sub-agent context
+- Complexity
+
+For simple boolean criteria checks, the main agent could self-verify.
+
+### 4. Redundant data fetching
+
+The PR diff was fetched separately in both `check_relevance` and `deep_review` steps instead of being passed along or cached.
+
+## Recommendations
+
+### For step instructions:
+
+1. **Use inline bash completion `$(command)`**: Instead of the orchestrating agent reading
+ data and passing it to sub-agents, embed commands directly in prompts:
+ ```
+ $(gh pr diff)
+ $(gh pr diff --name-only)
+ ```
+ This executes when the sub-agent spawns, avoiding token overhead in the main conversation.
+
+2. **Use relevance segmentation**: The deep_review step should explicitly use the "Relevant files" list from each expert's relevance assessment to scope what content is sent to each expert.
+
+3. **Prefer diffs over full files**: For review tasks, send the diff plus minimal context (function signatures, class definitions) rather than entire files.
+
+4. **Skip validation sub-agents for simple criteria**: If quality criteria are simple boolean checks (e.g., "output file exists", "all experts invoked"), the main agent can verify these directly.
+
+### For job design:
+
+1. **Let sub-agents fetch their own data**: Using `$(command)` in prompts means sub-agents
+ get fresh data directly without the orchestrator reading and forwarding it.
+
+2. **Consider expert-specific prompts**: Generate different prompts for each expert containing only their relevant files, rather than one massive prompt for all.
+
+## Key Takeaway
+
+The experts system's value comes from specialized, focused review. Sending all content to all experts defeats this purpose and wastes tokens. Job steps that invoke multiple experts should segment the workload based on each expert's declared relevance.
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/templates/agents.md.template b/.deepwork/experts/experts/templates/agents.md.template
similarity index 88%
rename from src/deepwork/standard_jobs/deepwork_jobs/templates/agents.md.template
rename to .deepwork/experts/experts/templates/agents.md.template
index c160921d..83681c0e 100644
--- a/src/deepwork/standard_jobs/deepwork_jobs/templates/agents.md.template
+++ b/.deepwork/experts/experts/templates/agents.md.template
@@ -1,4 +1,4 @@
-# Project Context for [Job Name]
+# Project Context for [Workflow Name]
## Codebase Structure
@@ -15,9 +15,9 @@
### File Organization
- [Pattern]: Reference `path/to/pattern/`
-## Job-Specific Context
+## Workflow-Specific Context
-### [Job Name]
+### [Workflow Name]
#### [Step Name]
- [Learning]: Reference `relevant/file.ext`
diff --git a/.deepwork/jobs/deepwork_jobs/templates/doc_spec.md.template b/.deepwork/experts/experts/templates/doc_spec.md.template
similarity index 100%
rename from .deepwork/jobs/deepwork_jobs/templates/doc_spec.md.template
rename to .deepwork/experts/experts/templates/doc_spec.md.template
diff --git a/.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example b/.deepwork/experts/experts/templates/step_instruction.md.example
similarity index 100%
rename from .deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example
rename to .deepwork/experts/experts/templates/step_instruction.md.example
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/templates/step_instruction.md.template b/.deepwork/experts/experts/templates/step_instruction.md.template
similarity index 79%
rename from src/deepwork/standard_jobs/deepwork_jobs/templates/step_instruction.md.template
rename to .deepwork/experts/experts/templates/step_instruction.md.template
index ddd19213..0486aaa9 100644
--- a/src/deepwork/standard_jobs/deepwork_jobs/templates/step_instruction.md.template
+++ b/.deepwork/experts/experts/templates/step_instruction.md.template
@@ -9,7 +9,7 @@
[Detailed instructions for completing this step, based on:
- The step's purpose
- Expected inputs and outputs
-- The job's overall context
+- The workflow's overall context
]
### Process
@@ -51,8 +51,7 @@
- [Quality criterion 1]
- [Quality criterion 2]
- [Quality criterion 3]
-- When all criteria are met, include `✓ Quality Criteria Met` in your response
## Context
-[Provide context from the job's overall description to help understand why this step matters and how it fits into the bigger picture]
+[Provide context from the workflow's overall description to help understand why this step matters and how it fits into the bigger picture]
diff --git a/.deepwork/jobs/deepwork_jobs/templates/job.yml.example b/.deepwork/experts/experts/templates/workflow.yml.example
similarity index 80%
rename from .deepwork/jobs/deepwork_jobs/templates/job.yml.example
rename to .deepwork/experts/experts/templates/workflow.yml.example
index 7cc6e3bb..b3558618 100644
--- a/.deepwork/jobs/deepwork_jobs/templates/job.yml.example
+++ b/.deepwork/experts/experts/templates/workflow.yml.example
@@ -1,6 +1,6 @@
-# Example: Competitive Research Job
+# Example: Competitive Research Workflow
#
-# This is a complete example of a job.yml file for reference.
+# This is a complete example of a workflow.yml file for reference.
name: competitive_research
version: "1.0.0"
@@ -12,7 +12,7 @@ description: |
changelog:
- version: "1.0.0"
- changes: "Initial job creation"
+ changes: "Initial workflow creation"
steps:
- id: identify_competitors
@@ -39,14 +39,10 @@ steps:
- research_notes.md
dependencies:
- identify_competitors
- hooks:
- after_agent:
- - prompt: |
- Verify the research meets criteria:
- 1. Each competitor has at least 3 data points
- 2. Sources are cited
- 3. Information is current (within last year)
- If ALL criteria are met, include `✓ Quality Criteria Met`.
+ quality_criteria:
+ - "Each competitor has at least 3 data points"
+ - "Sources are cited"
+ - "Information is current (within last year)"
- id: comparative_analysis
name: "Comparative Analysis"
diff --git a/.deepwork/jobs/deepwork_jobs/templates/job.yml.template b/.deepwork/experts/experts/templates/workflow.yml.template
similarity index 65%
rename from .deepwork/jobs/deepwork_jobs/templates/job.yml.template
rename to .deepwork/experts/experts/templates/workflow.yml.template
index 7dcf34e9..9fc65955 100644
--- a/.deepwork/jobs/deepwork_jobs/templates/job.yml.template
+++ b/.deepwork/experts/experts/templates/workflow.yml.template
@@ -1,13 +1,13 @@
-# DeepWork Job Specification Template
+# DeepWork Workflow Specification Template
#
-# This template shows the structure of a job.yml file.
+# This template shows the structure of a workflow.yml file.
# Replace placeholders in [brackets] with actual values.
-name: [job_name]
+name: [workflow_name]
version: "1.0.0"
-summary: "[Brief one-line summary of what this job accomplishes - max 200 chars]"
+summary: "[Brief one-line summary of what this workflow accomplishes - max 200 chars]"
description: |
- [Detailed multi-line description of the job's purpose, process, and goals.
+ [Detailed multi-line description of the workflow's purpose, process, and goals.
This should explain:
- What problem this workflow solves
@@ -18,7 +18,7 @@ description: |
changelog:
- version: "1.0.0"
- changes: "Initial job creation"
+ changes: "Initial workflow creation"
steps:
- id: [step_id]
@@ -35,15 +35,11 @@ steps:
- [output_filename_or_path] # e.g., "report.md" or "reports/analysis.md"
dependencies: [] # List of step IDs that must complete first
# Optional: Delegate to a specific agent type (uses context: fork)
- # agent: general-purpose # or other agent type
- # Optional: Quality validation hooks
- hooks:
- after_agent:
- - prompt: |
- Verify this step's output meets quality criteria:
- 1. [Criterion 1]
- 2. [Criterion 2]
- If ALL criteria are met, include `✓ Quality Criteria Met`.
+ # agent: experts # or other agent type
+ # Optional: Quality validation criteria
+ quality_criteria:
+ - "[Criterion 1]"
+ - "[Criterion 2]"
- id: [another_step]
name: "[Another Step]"
diff --git a/.deepwork/experts/experts/topics/discovery_descriptions.md b/.deepwork/experts/experts/topics/discovery_descriptions.md
new file mode 100644
index 00000000..f68112b9
--- /dev/null
+++ b/.deepwork/experts/experts/topics/discovery_descriptions.md
@@ -0,0 +1,83 @@
+---
+name: Writing Discovery Descriptions
+keywords:
+ - discovery
+ - description
+ - routing
+ - selection
+last_updated: 2025-01-30
+---
+
+# Writing Effective Discovery Descriptions
+
+The `discovery_description` determines when your expert gets invoked. It's the
+"elevator pitch" that helps the system route queries to the right expert.
+
+## Purpose
+
+Discovery descriptions are used by other parts of the system to decide:
+- Whether to suggest this expert for a task
+- Which expert to invoke when multiple could apply
+- How to present the expert to users
+
+## Anatomy of a Good Description
+
+```yaml
+discovery_description: |
+ Ruby on Rails ActiveJob - background job processing including
+ queue configuration, retry strategies, error handling, and
+ integration with queue backends like Sidekiq and SolidQueue.
+```
+
+Components:
+1. **Domain identifier**: "Ruby on Rails ActiveJob"
+2. **Core capability**: "background job processing"
+3. **Specific coverage**: "queue configuration, retry strategies..."
+4. **Boundaries**: "integration with queue backends like..."
+
+## Guidelines
+
+### Be Specific
+Bad: "Helps with background jobs"
+Good: "Rails ActiveJob background processing - queues, retries, error handling"
+
+### Include Key Terms
+Include terms users would search for. If someone asks about "Sidekiq retries",
+the description should contain those words.
+
+### Set Boundaries
+Indicate what's in and out of scope. "...including X, Y, Z" signals coverage.
+
+### Keep It Scannable
+1-3 sentences max. The system needs to quickly evaluate relevance.
+
+### Avoid Marketing Speak
+Bad: "The ultimate guide to mastering background jobs"
+Good: "ActiveJob configuration, error handling, and queue backend integration"
+
+## Examples
+
+### Too Vague
+```yaml
+discovery_description: Helps with Rails development
+```
+
+### Too Narrow
+```yaml
+discovery_description: How to configure exponential backoff in ActiveJob
+```
+
+### Just Right
+```yaml
+discovery_description: |
+ Rails ActiveJob expertise - background job processing, queue
+ configuration, retry strategies, error handling, and integration
+ with Sidekiq, SolidQueue, and other queue backends.
+```
+
+## Testing Your Description
+
+Ask yourself:
+1. If I had this problem, would I find this expert?
+2. Does it differentiate from similar experts?
+3. Can I tell what's covered in 5 seconds?
diff --git a/.deepwork/experts/experts/topics/expert_design_patterns.md b/.deepwork/experts/experts/topics/expert_design_patterns.md
new file mode 100644
index 00000000..3ce13a61
--- /dev/null
+++ b/.deepwork/experts/experts/topics/expert_design_patterns.md
@@ -0,0 +1,69 @@
+---
+name: Expert Design Patterns
+keywords:
+ - patterns
+ - structure
+ - organization
+ - best practices
+last_updated: 2025-01-30
+---
+
+# Expert Design Patterns
+
+Common patterns for structuring effective experts.
+
+## The Layered Knowledge Pattern
+
+Structure expertise from general to specific:
+
+1. **full_expertise**: Core concepts, decision frameworks, common patterns
+2. **topics/**: Deep dives into specific subjects
+3. **learnings/**: Concrete experiences and edge cases
+
+This mirrors how humans learn - start with foundations, then specialize.
+
+## The Problem-Solution Pattern
+
+For domains centered around solving problems:
+
+- **full_expertise**: Problem categories, diagnostic approaches, solution frameworks
+- **topics/**: Specific problem types with detailed solutions
+- **learnings/**: Real debugging sessions and unexpected fixes
+
+Works well for: troubleshooting guides, error handling, debugging domains.
+
+## The Reference Pattern
+
+For domains with lots of factual information:
+
+- **full_expertise**: Overview, when to use what, quick reference
+- **topics/**: Detailed reference on specific APIs, configs, options
+- **learnings/**: Gotchas and undocumented behaviors
+
+Works well for: API documentation, configuration guides, tool references.
+
+## The Process Pattern
+
+For domains with sequential workflows:
+
+- **full_expertise**: Overall process, decision points, success criteria
+- **topics/**: Detailed steps for each phase
+- **learnings/**: Process failures and improvements
+
+Works well for: deployment procedures, review processes, onboarding.
+
+## Anti-Patterns to Avoid
+
+### The Kitchen Sink
+Cramming everything into full_expertise. If it's over 5 pages, split into topics.
+
+### The Empty Shell
+Creating expert.yml with minimal content and empty topics/learnings folders.
+Start with meaningful content or don't create the expert yet.
+
+### The Stale Expert
+Never updating after initial creation. Set a reminder to review quarterly.
+
+### The Duplicate Expert
+Creating overlapping experts. Better to have one comprehensive expert than
+several fragmented ones.
diff --git a/.deepwork/experts/experts/topics/hooks_and_validation.md b/.deepwork/experts/experts/topics/hooks_and_validation.md
new file mode 100644
index 00000000..96d2d037
--- /dev/null
+++ b/.deepwork/experts/experts/topics/hooks_and_validation.md
@@ -0,0 +1,179 @@
+---
+name: Hooks and Validation
+keywords:
+ - hooks
+ - validation
+ - quality
+ - lifecycle
+last_updated: 2025-02-01
+---
+
+# Hooks and Validation
+
+How to use lifecycle hooks for quality validation in DeepWork workflows.
+
+## Lifecycle Hook Events
+
+DeepWork supports three generic hook events:
+
+| Event | When it fires | Use case |
+|-------|---------------|----------|
+| `after_agent` | After agent finishes responding | Quality validation, output verification |
+| `before_tool` | Before agent uses a tool | Pre-tool checks, validation |
+| `before_prompt` | When user submits a prompt | Session setup, context loading |
+
+## Platform Mapping
+
+Hooks are mapped to platform-specific event names:
+
+| Generic | Claude Code |
+|---------|-------------|
+| `after_agent` | `Stop`, `SubagentStop` |
+| `before_tool` | `PreToolUse` |
+| `before_prompt` | `UserPromptSubmit` |
+
+Note: Gemini CLI does not support skill-level hooks (only global hooks).
+
+## Hook Action Types
+
+### Inline Prompt
+
+Best for simple validation criteria:
+
+```yaml
+hooks:
+ after_agent:
+ - prompt: |
+ Verify the output meets these criteria:
+ 1. Contains at least 5 competitors
+ 2. Each has a description
+ 3. Sources are cited
+```
+
+**Important**: Prompt hooks are currently parsed but NOT executed by Claude Code.
+This is a documented limitation. Use script hooks for actual enforcement.
+
+### Prompt File
+
+For detailed/reusable criteria:
+
+```yaml
+hooks:
+ after_agent:
+ - prompt_file: hooks/quality_check.md
+```
+
+The prompt file is read and its content is embedded in the generated skill.
+Same limitation applies - parsed but not executed.
+
+### Script Hook
+
+For programmatic validation (actually executed):
+
+```yaml
+hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+```
+
+Scripts are shell scripts that can:
+- Run test suites
+- Lint output files
+- Check for required content
+- Validate file formats
+
+Exit code 0 = pass, non-zero = fail.
+
+## Script Hook Example
+
+Create `.deepwork/experts/[expert]/workflows/[workflow]/hooks/validate.sh`:
+
+```bash
+#!/bin/bash
+# Validate research output
+
+OUTPUT_FILE="research_notes.md"
+
+if [ ! -f "$OUTPUT_FILE" ]; then
+ echo "ERROR: $OUTPUT_FILE not found"
+ exit 1
+fi
+
+# Check minimum content
+LINES=$(wc -l < "$OUTPUT_FILE")
+if [ "$LINES" -lt 50 ]; then
+ echo "ERROR: Output has only $LINES lines, expected at least 50"
+ exit 1
+fi
+
+echo "Validation passed"
+exit 0
+```
+
+Make it executable:
+```bash
+chmod +x .deepwork/experts/[expert]/workflows/[workflow]/hooks/validate.sh
+```
+
+## Combining Multiple Hooks
+
+```yaml
+hooks:
+ after_agent:
+ - script: hooks/lint.sh
+ - script: hooks/run_tests.sh
+ - prompt: "Verify documentation is complete"
+```
+
+Hooks run in order. Script hooks are executed; prompt hooks are for reference.
+
+## Quality Criteria Alternative
+
+For simple validation, use declarative `quality_criteria` instead of hooks:
+
+```yaml
+steps:
+ - id: research
+ quality_criteria:
+ - "**Data Coverage**: Each competitor has at least 3 data points"
+ - "**Source Attribution**: All facts are cited"
+ - "**Relevance**: All competitors are in the target market"
+```
+
+Quality criteria are rendered in the skill with instructions to use a
+sub-agent (Haiku model) for review:
+
+1. Agent completes work
+2. Spawns sub-agent to review against criteria
+3. Fixes any issues identified
+4. Repeats until sub-agent confirms all criteria pass
+
+This is the recommended approach for most validation needs - it's more
+flexible than scripts and actually works with Claude Code.
+
+## When to Use Each Approach
+
+| Approach | When to use |
+|----------|-------------|
+| `quality_criteria` | Most cases - subjective quality checks |
+| Script hooks | Objective checks (tests, linting, format validation) |
+| Prompt hooks | Documentation only (not currently executed) |
+
+## Generated Skill Output
+
+For script hooks, the generated skill includes:
+
+```yaml
+hooks:
+ Stop:
+ - hooks:
+ - type: command
+ command: ".deepwork/experts/expert_name/workflows/workflow_name/hooks/validate.sh"
+ SubagentStop:
+ - hooks:
+ - type: command
+ command: ".deepwork/experts/expert_name/workflows/workflow_name/hooks/validate.sh"
+```
+
+Both `Stop` and `SubagentStop` are registered so hooks fire for both
+the main agent and any sub-agents.
diff --git a/.deepwork/experts/experts/topics/skill_generation.md b/.deepwork/experts/experts/topics/skill_generation.md
new file mode 100644
index 00000000..ea943b17
--- /dev/null
+++ b/.deepwork/experts/experts/topics/skill_generation.md
@@ -0,0 +1,163 @@
+---
+name: Skill Generation
+keywords:
+ - sync
+ - templates
+ - jinja
+ - skills
+ - commands
+last_updated: 2025-02-01
+---
+
+# Skill Generation
+
+How DeepWork generates platform-specific skills from expert definitions.
+
+## The Sync Process
+
+Running `deepwork sync`:
+
+1. Loads `config.yml` to get configured platforms
+2. Discovers all expert directories in `.deepwork/experts/`
+3. For each expert, discovers workflows in `workflows/` subdirectory
+4. Parses each `workflow.yml` into dataclasses
+5. For each workflow and platform, generates skills using Jinja2 templates
+6. Writes skill files to platform-specific directories
+7. Syncs hooks and permissions to platform settings
+
+## Generated Skill Types
+
+### Workflow Meta-Skill
+- One per workflow
+- Routes user intent to appropriate step
+- Lists available steps in the workflow
+- Claude: `.claude/skills/[expert-name].[workflow-name]/SKILL.md`
+
+### Step Skills
+- One per step
+- Contains full instructions, inputs, outputs
+- Includes workflow position and navigation
+- Claude: `.claude/skills/[expert-name].[step-id]/SKILL.md`
+
+## Skill Naming Convention
+
+Skills use 2-part naming: `{expert-name}.{step-id}`
+
+Examples:
+- `experts.define` - The define step in the experts expert
+- `experts.implement` - The implement step
+- `deepwork-rules.define` - The define step in deepwork-rules expert
+
+## Template Variables
+
+Templates receive rich context including:
+
+**Expert Context**:
+- `expert_name` - The expert identifier (hyphenated)
+
+**Workflow Context**:
+- `workflow_name`, `workflow_version`, `workflow_summary`, `workflow_description`
+- `total_steps`, `execution_entries`
+
+**Step Context**:
+- `step_id`, `step_name`, `step_description`
+- `instructions_content` (full markdown from instructions file)
+- `user_inputs`, `file_inputs`, `outputs`, `dependencies`
+- `exposed`, `agent`
+- `workflow_step_number`, `workflow_total_steps`
+- `next_step`, `prev_step`
+
+**Quality & Hooks**:
+- `quality_criteria` (array of strings)
+- `hooks` (dict by platform event name)
+
+## Template Location
+
+Templates live in `src/deepwork/templates/[platform]/`:
+
+```
+templates/
+├── claude/
+│ ├── skill-workflow-meta.md.jinja
+│ ├── skill-workflow-step.md.jinja
+│ └── agent-expert.md.jinja
+└── gemini/
+ ├── skill-workflow-meta.toml.jinja
+ └── skill-workflow-step.toml.jinja
+```
+
+## Platform Differences
+
+**Claude Code**:
+- Markdown format with YAML frontmatter
+- Uses `---` delimited frontmatter for metadata
+- Hook events: `Stop`, `SubagentStop`, `PreToolUse`, `UserPromptSubmit`
+- Skills directory: `.claude/skills/`
+- Agents directory: `.claude/agents/`
+
+**Gemini CLI**:
+- TOML format
+- No skill-level hooks (global only)
+- Skills directory: `.gemini/skills/`
+
+## The Generator Class
+
+`ExpertGenerator` in `core/experts_generator.py`:
+
+```python
+generator = ExpertGenerator()
+
+# Generate all skills for an expert
+paths = generator.generate_all_expert_skills(
+ expert=expert_definition,
+ adapter=claude_adapter,
+ output_dir=project_path,
+ project_root=project_path
+)
+
+# Generate single step skill
+path = generator.generate_workflow_step_skill(
+ expert=expert_def,
+ workflow=workflow,
+ step=step,
+ adapter=adapter,
+ output_dir=output_dir
+)
+```
+
+## Doc Spec Integration
+
+When outputs reference doc specs, the generator:
+
+1. Loads doc spec file using `DocSpecParser`
+2. Extracts quality criteria, target audience, example document
+3. Includes this context in template variables
+4. Generated skill displays doc spec requirements inline
+
+## Skill Frontmatter
+
+Claude skills have YAML frontmatter:
+
+```yaml
+---
+name: experts.define
+description: "Step description"
+user-invocable: false # when exposed: false
+context: fork # when agent is specified
+agent: experts
+hooks:
+ Stop:
+ - hooks:
+ - type: command
+ command: ".deepwork/experts/experts/workflows/new_workflow/hooks/validate.sh"
+---
+```
+
+## Permissions Syncing
+
+After generating skills, adapters sync permissions:
+
+- Base permissions (read `.deepwork/tmp/**`)
+- Skill invocation permissions (`Skill(expert.step_id)`)
+
+Permissions are added to `.claude/settings.json` in the `permissions.allow` array.
diff --git a/.deepwork/experts/experts/topics/step_delegation.md b/.deepwork/experts/experts/topics/step_delegation.md
new file mode 100644
index 00000000..042f68ea
--- /dev/null
+++ b/.deepwork/experts/experts/topics/step_delegation.md
@@ -0,0 +1,145 @@
+---
+name: Step Delegation to Experts
+keywords:
+ - agent
+ - expert
+ - delegation
+ - context fork
+ - sub-agent
+last_updated: 2025-02-01
+---
+
+# Step Delegation to Experts
+
+How to make workflow steps run via experts using the `agent` field.
+
+## The `agent` Field
+
+The `agent` field in a step definition specifies which expert should execute that step. This is the primary mechanism for making steps "run via" an expert.
+
+```yaml
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Creates a workflow.yml specification..."
+ instructions_file: steps/define.md
+ agent: experts # <-- This step runs via the experts expert
+ outputs:
+ - workflow.yml
+```
+
+## What Happens When You Set `agent`
+
+When a step has `agent: expert-name`, the generated skill includes:
+
+1. **`context: fork`** - The step runs in an isolated context (sub-agent)
+2. **`agent: expert-name`** - The expert's knowledge is loaded into that context
+
+The resulting skill frontmatter looks like:
+
+```yaml
+---
+name: my-expert.define
+description: "Creates a workflow.yml specification..."
+context: fork
+agent: experts
+---
+```
+
+## How Expert Knowledge Flows
+
+When the skill runs:
+
+1. A forked context (sub-agent) is created
+2. The expert file (e.g., `.claude/agents/dwe_experts.md`) is loaded
+3. All expert topics, learnings, and domain knowledge become available
+4. The step instructions execute with this enhanced context
+5. The sub-agent completes and returns to the parent context
+
+## Real-World Example: experts
+
+The `experts` expert itself uses expert delegation. The new_workflow workflow steps run via the `experts` expert:
+
+```yaml
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Creates a workflow.yml specification..."
+ instructions_file: steps/define.md
+ agent: experts # Runs with workflow schema knowledge
+ outputs:
+ - file: workflow.yml
+ doc_spec: .deepwork/doc_specs/workflow_spec.md
+
+ - id: review_spec
+ name: "Review Workflow Specification"
+ description: "Reviews workflow.yml against quality criteria..."
+ instructions_file: steps/review_spec.md
+ agent: experts # Same expert, different step
+ inputs:
+ - file: workflow.yml
+ from_step: define
+ outputs:
+ - file: workflow.yml
+ doc_spec: .deepwork/doc_specs/workflow_spec.md
+
+ - id: implement
+ name: "Implement Workflow Steps"
+ description: "Generates step instruction files..."
+ instructions_file: steps/implement.md
+ agent: experts # Expert knowledge helps write good instructions
+ inputs:
+ - file: workflow.yml
+ from_step: review_spec
+ outputs:
+ - steps/
+```
+
+## When to Use Expert Delegation
+
+Use `agent: expert-name` when:
+
+1. **Domain expertise is needed** - The step requires specialized knowledge that an expert provides
+2. **Consistency across steps** - Multiple steps in a workflow should use the same expert context
+3. **Complex validation** - The expert has knowledge about quality criteria or best practices
+4. **Template awareness** - The expert knows about file formats, schemas, or conventions
+
+## Available Experts
+
+Standard experts that ship with DeepWork:
+
+- `experts` - Expert on expert creation, workflow definitions, skill generation
+- `deepwork-rules` - Expert on rule creation and file-change triggers
+
+Custom experts can be created in `.deepwork/experts/` and referenced by name.
+
+## Expert vs. Sub-Agent Quality Review
+
+Note the difference between:
+
+1. **Expert delegation** (`agent: experts`) - The entire step runs with expert knowledge loaded
+2. **Sub-agent quality review** (`quality_criteria` field) - A Haiku sub-agent validates outputs against criteria
+
+These can be combined - a step can run via an expert AND have quality criteria validated by a sub-agent:
+
+```yaml
+steps:
+ - id: implement
+ instructions_file: steps/implement.md
+ agent: experts # Expert knowledge during execution
+ quality_criteria: # Sub-agent validates outputs
+ - "All instruction files are complete"
+ - "No placeholder content remains"
+```
+
+## Troubleshooting
+
+**Step not using expert knowledge?**
+- Verify `agent: expert-name` is set in workflow.yml
+- Run `deepwork sync` to regenerate skills
+- Check the generated skill has `agent:` in its frontmatter
+
+**Expert not found?**
+- Ensure the expert exists in `.deepwork/experts/[expert-name]/`
+- Run `deepwork sync` to generate the expert agent file
+- Check `.claude/agents/dwe_[expert-name].md` exists
diff --git a/.deepwork/experts/experts/topics/step_instructions.md b/.deepwork/experts/experts/topics/step_instructions.md
new file mode 100644
index 00000000..df2594c1
--- /dev/null
+++ b/.deepwork/experts/experts/topics/step_instructions.md
@@ -0,0 +1,187 @@
+---
+name: Writing Step Instructions
+keywords:
+ - instructions
+ - steps
+ - markdown
+ - writing
+ - best practices
+last_updated: 2025-02-01
+---
+
+# Writing Step Instructions
+
+Best practices for writing effective step instruction files.
+
+## File Location
+
+Step instructions live in `.deepwork/experts/[expert]/workflows/[workflow]/steps/[step_id].md`.
+
+The path is specified in workflow.yml via `instructions_file`:
+```yaml
+steps:
+ - id: identify_competitors
+ instructions_file: steps/identify_competitors.md
+```
+
+## Recommended Structure
+
+### 1. Objective Section
+
+Start with a clear objective statement:
+
+```markdown
+## Objective
+
+Create a comprehensive list of competitors in the target market by
+systematically researching industry players and their offerings.
+```
+
+### 2. Task Section
+
+Detailed step-by-step process:
+
+```markdown
+## Task
+
+### Step 1: Understand the Market
+
+Ask structured questions to gather context:
+- What industry or market segment?
+- What product category?
+- Geographic scope?
+
+### Step 2: Research Sources
+
+Search for competitors using:
+1. Industry databases and reports
+2. Google searches for market leaders
+3. Customer review sites
+...
+```
+
+### 3. Input Handling
+
+If the step has user inputs, explicitly request them:
+
+```markdown
+## Inputs
+
+Before proceeding, gather the following from the user:
+- **market_segment**: Target market for analysis
+- **product_category**: Specific product/service category
+
+Ask questions one at a time and wait for responses.
+```
+
+### 4. Output Format
+
+Show what good output looks like:
+
+```markdown
+## Output Format
+
+Create `competitors_list.md` with the following structure:
+
+```markdown
+# Competitors List
+
+## Market: [market_segment]
+
+### Competitor 1: Acme Corp
+- **Website**: acme.com
+- **Description**: Brief overview
+- **Key Products**: Product A, Product B
+```
+
+### 5. Quality Criteria
+
+Define how to verify the step is complete:
+
+```markdown
+## Quality Criteria
+
+- At least 5 competitors identified
+- Each competitor has description and key products
+- Sources are cited for all information
+- List is relevant to the specified market
+```
+
+## Key Phrases
+
+### "Ask structured questions"
+
+When gathering user input, use this phrase and ask questions one at a time:
+
+```markdown
+Ask structured questions to understand the user's requirements.
+Start with: What is your target market?
+```
+
+**Important**: Present one question, wait for the response, then ask the next.
+Do not output all questions as a numbered list - this results in poor UX
+where the user must scroll through and answer multiple questions at once.
+
+### "Use the Skill tool to invoke"
+
+For workflow continuation:
+
+```markdown
+## On Completion
+
+1. Verify outputs are created
+2. Use the Skill tool to invoke `/experts.next_step`
+```
+
+## Supplementary Files
+
+Place additional reference materials in `steps/`:
+
+```
+steps/
+├── identify_competitors.md
+├── research_competitors.md
+└── competitor_template.md # supplementary reference
+```
+
+Reference them using full paths:
+```markdown
+Use the template in `.deepwork/experts/my_expert/workflows/competitive_research/steps/competitor_template.md`
+to structure each competitor profile.
+```
+
+## Anti-Patterns to Avoid
+
+### Vague Instructions
+Bad: "Research the competitors"
+Good: "Search each competitor's website, LinkedIn, and review sites to gather..."
+
+### Missing Outputs
+Bad: "Create a report"
+Good: "Create `research_notes.md` with sections for each competitor..."
+
+### Skipping Inputs
+Bad: Assume inputs are available
+Good: "Read `competitors_list.md` from the previous step. If it doesn't exist..."
+
+### Generic Quality Criteria
+Bad: "Output should be good quality"
+Good: "Each competitor profile includes at least 3 data points with sources"
+
+## Instruction Length
+
+- Keep instructions focused and actionable
+- Aim for 1-3 pages of content
+- Extract lengthy examples into separate template files
+- Use bullet points over paragraphs where appropriate
+
+## Variables in Instructions
+
+Instructions can reference workflow-level context. The generated skill includes:
+- Expert and workflow name
+- Step position in workflow
+- Dependencies and next steps
+- All inputs and outputs
+
+You don't need to repeat this metadata in instructions - it's included
+automatically in the generated skill.
diff --git a/.deepwork/experts/experts/topics/workflow_yml_schema.md b/.deepwork/experts/experts/topics/workflow_yml_schema.md
new file mode 100644
index 00000000..697bd6c3
--- /dev/null
+++ b/.deepwork/experts/experts/topics/workflow_yml_schema.md
@@ -0,0 +1,181 @@
+---
+name: workflow.yml Schema Reference
+keywords:
+ - schema
+ - yaml
+ - validation
+ - fields
+ - structure
+ - workflow
+last_updated: 2025-02-01
+---
+
+# workflow.yml Schema Reference
+
+Complete reference for the workflow.yml specification file.
+
+## Required Fields
+
+### name
+- Type: string
+- Pattern: `^[a-z][a-z0-9_]*$`
+- Must start with lowercase letter, can contain lowercase letters, numbers, underscores
+- Must match the workflow folder name
+- Examples: `new_workflow`, `review_pr`, `learn`
+
+### version
+- Type: string
+- Pattern: `^\d+\.\d+\.\d+$`
+- Semantic versioning format
+- Examples: `1.0.0`, `2.1.3`
+
+### summary
+- Type: string
+- Length: 1-200 characters
+- Brief one-line description of the workflow
+- Used in skill descriptions and menus
+
+### steps
+- Type: array
+- Minimum items: 1
+- Each item is a step definition object
+
+## Optional Fields
+
+### description
+- Type: string
+- Multi-line detailed explanation
+- Included in generated skill files for context
+- Good for: problem solved, process overview, target users
+
+### execution_order
+- Type: array
+- Explicit step ordering with concurrent support
+- See Execution Order section
+
+### changelog
+- Type: array
+- Version history entries
+- Each entry has `version` (string) and `changes` (string)
+
+## Step Schema
+
+### Required Step Fields
+
+```yaml
+steps:
+ - id: identify_competitors # unique within expert, lowercase_underscores
+ name: "Identify Competitors" # human-readable
+ description: "Find and list..." # what it does
+ instructions_file: steps/identify.md # path relative to workflow dir
+ outputs: # at least one output
+ - competitors_list.md
+```
+
+### Optional Step Fields
+
+```yaml
+ - id: research
+ # ... required fields ...
+ inputs:
+ - name: market_segment
+ description: "Target market"
+ - file: competitors_list.md
+ from_step: identify_competitors
+ dependencies:
+ - identify_competitors
+ exposed: true # show in user menus
+ quality_criteria:
+ - "All competitors have descriptions"
+ - "Sources are cited"
+ agent: experts # delegate to expert (see Agent Delegation)
+ hooks:
+ after_agent:
+ - script: hooks/validate.sh
+```
+
+### Agent Delegation (The `agent` Field)
+
+The `agent` field specifies which expert should execute this step. When set:
+1. The generated skill includes `context: fork` (runs in isolated context)
+2. The generated skill includes `agent: [expert-name]` (loads that expert's knowledge)
+3. The step runs with all the expert's topics, learnings, and domain knowledge available
+
+**This is how you make a step "run via" an expert.**
+
+```yaml
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Creates a workflow.yml specification..."
+ instructions_file: steps/define.md
+ agent: experts # Runs with experts expert loaded
+ outputs:
+ - workflow.yml
+```
+
+Common agent values:
+- `experts` - Expert for DeepWork expert and workflow creation
+- `deepwork-rules` - Expert for DeepWork rule creation
+- Custom experts you've defined in `.deepwork/experts/`
+
+See the **Step Delegation** topic for detailed examples and patterns.
+
+## Output Formats
+
+Simple string:
+```yaml
+outputs:
+ - report.md
+ - data/
+```
+
+With doc spec:
+```yaml
+outputs:
+ - file: reports/analysis.md
+ doc_spec: .deepwork/doc_specs/analysis.md
+```
+
+Doc spec path must match pattern: `^\.deepwork/doc_specs/[a-z][a-z0-9_-]*\.md$`
+
+## Execution Order
+
+By default, steps execute in the order defined. Use `execution_order` for explicit control:
+
+Sequential:
+```yaml
+execution_order:
+ - identify
+ - research
+ - analyze
+ - report
+```
+
+Concurrent steps:
+```yaml
+execution_order:
+ - identify
+ - [research_a, research_b] # parallel execution
+ - synthesize
+```
+
+## Hook Schema
+
+```yaml
+hooks:
+ after_agent:
+ - prompt: "Verify criteria are met" # inline
+ - prompt_file: hooks/check.md # from file
+ - script: hooks/run_tests.sh # shell script
+```
+
+Each hook action must have exactly one of: `prompt`, `prompt_file`, or `script`.
+
+## Validation Rules
+
+1. Step IDs must be unique within the expert (across all workflows)
+2. Dependencies must reference existing step IDs
+3. File inputs with `from_step` must have that step in dependencies
+4. No circular dependencies allowed
+5. Execution order steps must reference existing step IDs
diff --git a/.deepwork/experts/experts/workflows/learn/steps/learn.md b/.deepwork/experts/experts/workflows/learn/steps/learn.md
new file mode 100644
index 00000000..9474d408
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/learn/steps/learn.md
@@ -0,0 +1,113 @@
+# Learn from Workflow Execution
+
+## Objective
+
+Reflect on the conversation to identify learnings from DeepWork workflow executions, improve workflow instructions with generalizable insights, and capture run-specific learnings in AGENTS.md files.
+
+## Task
+
+Analyze conversation history to extract learnings, then apply them:
+- **Generalizable learnings** -> Update workflow instruction files
+- **Bespoke learnings** (run-specific) -> Add to AGENTS.md in the working folder
+
+### Step 1: Analyze Conversation for Workflow Executions
+
+1. **Scan the conversation** for DeepWork slash commands (`/expert-name.step_id`)
+2. **Identify the target folder** - the deepest common folder for all work on the topic
+3. **If no workflow specified**, ask which workflow to learn from
+
+### Step 2: Identify Confusion and Inefficiency
+
+Review the conversation for:
+
+**Confusion signals**:
+- Unnecessary questions the agent asked
+- Misunderstandings about step requirements
+- Incorrect outputs needing correction
+- Ambiguous instructions causing wrong interpretations
+
+**Inefficiency signals**:
+- Extra iterations needed
+- Repeated information
+- Missing context
+- Unclear dependencies
+
+**Error patterns**:
+- Failed validations and why
+- Misunderstood quality criteria
+- Unhandled edge cases
+
+**Success patterns**:
+- What worked well
+- Efficient approaches worth preserving
+- Good examples to add to instructions
+
+### Step 3: Classify Learnings
+
+For each learning, determine if it is:
+
+**Generalizable** (update instructions):
+- Would help ANY future run of this workflow
+- Addresses unclear or missing guidance
+- Fixes incorrect assumptions
+- Adds helpful examples
+
+**Doc spec-related** (update doc spec files):
+- Improvements to document quality criteria
+- Changes to document structure or format
+
+**Bespoke** (add to AGENTS.md):
+- Specific to THIS project/codebase/run
+- Depends on local conventions
+- References specific files or paths
+- Would not apply to other uses
+
+### Step 4: Update Workflow Instructions (Generalizable)
+
+For each generalizable learning:
+
+1. Locate `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`
+2. Make targeted improvements:
+ - Add missing context
+ - Include helpful examples
+ - Clarify ambiguous instructions
+ - Update quality criteria
+3. Keep instructions concise - avoid redundancy
+4. Preserve structure (Objective, Task, Output Format, Quality Criteria)
+
+### Step 5: Update Doc Specs (if applicable)
+
+If doc spec-related learnings identified:
+
+1. Locate doc spec at `.deepwork/doc_specs/[doc_spec_name].md`
+2. Update quality_criteria, example document, or metadata as needed
+
+### Step 6: Create/Update AGENTS.md (Bespoke)
+
+1. Place in the deepest common folder for the topic
+2. Use file references instead of duplicating content: "See `path/to/file.ext` for [description]"
+
+**Good patterns** (references):
+```markdown
+- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
+- Configuration schema: Defined in `config/schema.json`
+```
+
+**Avoid** (duplicating):
+```markdown
+- API endpoints should return JSON with this format: { status: ..., data: ... }
+```
+
+### Step 7: Update Workflow Version and Sync
+
+If instructions were modified:
+
+1. Bump version in workflow.yml (patch for improvements, minor for quality criteria changes)
+2. Add changelog entry
+3. Run `deepwork sync`
+
+## Output
+
+- Updated workflow instructions (generalizable learnings)
+- Updated doc specs (if applicable)
+- AGENTS.md with bespoke learnings in the correct working folder
diff --git a/.deepwork/experts/experts/workflows/learn/workflow.yml b/.deepwork/experts/experts/workflows/learn/workflow.yml
new file mode 100644
index 00000000..94502252
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/learn/workflow.yml
@@ -0,0 +1,21 @@
+name: learn
+version: "1.0.0"
+summary: "Analyze conversation history to improve workflow instructions and capture learnings"
+
+description: |
+ Reflect on the conversation to identify learnings from DeepWork workflow executions,
+ improve workflow instructions with generalizable insights, and capture run-specific
+ learnings in AGENTS.md files.
+
+steps:
+ - id: learn
+ name: "Learn from Workflow Execution"
+ description: "Analyze conversation to extract learnings and improve instructions"
+ instructions_file: steps/learn.md
+ inputs:
+ - name: expert_name
+ description: "Name of the expert whose workflow to learn from (optional - will scan conversation if not provided)"
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/"
+ - "AGENTS.md"
+ exposed: true
diff --git a/.deepwork/experts/experts/workflows/new_workflow/steps/define.md b/.deepwork/experts/experts/workflows/new_workflow/steps/define.md
new file mode 100644
index 00000000..6ca8b721
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/new_workflow/steps/define.md
@@ -0,0 +1,104 @@
+# Define Workflow Specification
+
+## Objective
+
+Create a `workflow.yml` specification file that defines a new DeepWork workflow by understanding the user's requirements through interactive questioning.
+
+## Task
+
+Guide the user through defining a workflow specification by asking structured questions. The output is **only** the `workflow.yml` file - step instruction files are created in the `implement` step.
+
+### Phase 1: Understand the Workflow Purpose
+
+Ask structured questions to understand the workflow:
+
+1. **Overall goal** - What complex task are they trying to accomplish? What domain (research, marketing, development, reporting)?
+
+2. **Success criteria** - What's the final deliverable? Who is the audience? What quality matters most?
+
+3. **Major phases** - What are the distinct stages from start to finish? Any dependencies between phases?
+
+4. **Expert context** - Which expert should this workflow belong to? Existing one or create new?
+
+### Phase 2: Detect Document-Oriented Workflows
+
+Check for document-focused patterns in the user's description:
+- Keywords: "report", "summary", "document", "monthly", "quarterly", "for stakeholders"
+- Final deliverable is a specific document type
+- Recurring documents with consistent structure
+
+**If detected**, offer to create a doc spec:
+1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first."
+2. Ask if they want to create a doc spec, use existing one, or skip
+
+**If creating a doc spec**, gather:
+- Document name and purpose
+- Target audience and frequency
+- Quality criteria (3-5, focused on the output document itself)
+- Document structure (sections, required elements)
+
+Create at `.deepwork/doc_specs/[doc_spec_name].md`.
+
+### Phase 3: Define Each Step
+
+For each major phase, gather:
+
+1. **Purpose** - What does this step accomplish? What are inputs and outputs?
+
+2. **Inputs**:
+ - User-provided parameters (e.g., topic, target audience)?
+ - Files from previous steps?
+
+3. **Outputs**:
+ - What files does this step produce?
+ - Format (markdown, YAML, JSON)?
+ - Where to save? (Use meaningful paths like `competitive_research/analysis.md`)
+ - Does this output have a doc spec?
+
+4. **Dependencies** - Which previous steps must complete first?
+
+5. **Process** - Key activities? Quality checks needed?
+
+6. **Agent Delegation** - Should this step run via a specific expert? Use `agent: experts` for domain-specific expertise.
+
+#### Output Path Guidelines
+
+- Place outputs in main repo, not dot-directories
+- Use workflow name as top-level folder for workflow-specific outputs
+- Include dates for periodic outputs that accumulate (monthly reports)
+- Omit dates for current-state outputs that get updated in place
+- Use `_dataroom` folders for supporting materials
+
+### Phase 4: Validate the Workflow
+
+After gathering all step information:
+
+1. **Review the flow** - Summarize the complete workflow, show how outputs feed into next steps
+2. **Check for gaps** - Undefined inputs? Unused outputs? Circular dependencies?
+3. **Confirm details** - Workflow name (lowercase_underscores), summary (max 200 chars), description (detailed), version (1.0.0)
+4. **Step ID uniqueness** - Step IDs must be unique within the expert, across all workflows
+
+### Phase 5: Create the Workflow
+
+Determine the expert and create the directory structure:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/steps
+```
+
+Create `workflow.yml` at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+**Validation rules**:
+- Workflow name: lowercase, underscores, no spaces (must match folder name)
+- Version: semantic versioning (1.0.0)
+- Summary: under 200 characters
+- Step IDs: unique within the expert, lowercase with underscores
+- Dependencies must reference existing steps
+- File inputs with `from_step` must be in dependencies
+- At least one output per step
+
+## Output
+
+**File**: `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+
+After creating the file, the next step will review it against quality criteria.
diff --git a/.deepwork/experts/experts/workflows/new_workflow/steps/implement.md b/.deepwork/experts/experts/workflows/new_workflow/steps/implement.md
new file mode 100644
index 00000000..129dc588
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/new_workflow/steps/implement.md
@@ -0,0 +1,99 @@
+# Implement Workflow Steps
+
+## Objective
+
+Generate the step instruction files for each step in the validated `workflow.yml` specification, then sync the skills.
+
+## Task
+
+Read the `workflow.yml` specification and create all necessary files to make the workflow functional, then sync the commands.
+
+### Step 1: Create Directory Structure
+
+Create the workflow directories:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/{steps,hooks}
+```
+
+This creates:
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/` - Main workflow directory
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/` - Step instruction files
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/hooks/` - Custom validation scripts
+
+**Note**: If directory exists from define step, skip or just create missing subdirectories.
+
+### Step 2: Read and Validate the Specification
+
+1. Read `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+2. Validate: name, version, summary, steps are present
+3. Check dependencies reference existing steps, no circular dependencies
+4. Verify file inputs match dependencies
+
+### Step 3: Generate Step Instruction Files
+
+For each step in workflow.yml, create `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`.
+
+**Guidelines**:
+
+1. **Use the workflow description** - It provides crucial context
+2. **Be specific** - Tailor instructions to the step's purpose, not generic
+3. **Provide examples** - Show what good output looks like
+4. **Explain the "why"** - Help understand the step's role in the workflow
+5. **Ask structured questions** - Steps with user inputs MUST explicitly tell the agent to "ask structured questions"
+6. **Align with hooks** - If step has hooks, ensure quality criteria match
+
+Each instruction file should include:
+- **Objective** - What this step accomplishes
+- **Task** - Detailed process
+- **Output Format** - Examples of expected outputs
+- **Quality Criteria** - How to verify completion
+
+### Step 4: Verify workflow.yml Location
+
+Ensure `workflow.yml` is at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+### Step 5: Sync Skills
+
+Run:
+
+```bash
+deepwork sync
+```
+
+This generates skills in `.claude/skills/` (or appropriate platform directory).
+
+### Step 6: Consider Rules
+
+After implementing, consider whether **rules** would help this workflow's domain.
+
+**What are rules?** Automated guardrails that trigger when certain files change, ensuring:
+- Documentation stays in sync
+- Team guidelines are followed
+- Quality standards are maintained
+
+**When to suggest rules:**
+- Does this workflow produce outputs that other files depend on?
+- Are there docs that should update when outputs change?
+- Could changes impact other parts of the project?
+
+**Examples**:
+| Workflow Type | Potential Rule |
+|---------------|----------------|
+| API Design | "Update API docs when endpoint definitions change" |
+| Competitive Research | "Update strategy docs when competitor analysis changes" |
+| Feature Development | "Update changelog when feature files change" |
+
+If you identify helpful rules, explain what they would do and offer: "Would you like me to create this rule? I can run `/deepwork-rules.define` to set it up."
+
+**Note**: Not every workflow needs rules. Only suggest when genuinely helpful.
+
+## Completion Checklist
+
+- [ ] workflow.yml in correct location
+- [ ] All step instruction files created (not stubs)
+- [ ] Instructions are specific and actionable
+- [ ] Output examples provided
+- [ ] Quality criteria defined for each step
+- [ ] `deepwork sync` executed successfully
+- [ ] Considered relevant rules for this workflow
diff --git a/.deepwork/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md b/.deepwork/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md
new file mode 100644
index 00000000..880b8363
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md
@@ -0,0 +1,72 @@
+# Review Workflow Specification
+
+## Objective
+
+Review the `workflow.yml` created in the define step against quality criteria, then iterate on fixes until all criteria pass.
+
+## Why This Step Exists
+
+The define step focuses on understanding user requirements. This review step ensures the specification meets quality standards before implementation. A fresh review catches issues that might be missed after being deeply involved in definition.
+
+## Task
+
+Review the workflow.yml against all quality criteria, fix any failures, and repeat until all pass.
+
+### Step 1: Read the Files
+
+Read the workflow specification:
+```
+.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml
+```
+
+### Step 2: Evaluate Against Quality Criteria
+
+Review the workflow.yml against these criteria:
+
+1. **Valid Identifier** - Workflow name is lowercase with underscores, matches folder name
+2. **Semantic Version** - Version follows X.Y.Z format (e.g., `1.0.0`)
+3. **Concise Summary** - Summary is under 200 characters and clearly describes the workflow
+4. **Rich Description** - Description is multi-line and explains: problem solved, process, expected outcomes, target users
+5. **Complete Steps** - Each step has: id, name, description, instructions_file, outputs
+6. **Unique Step IDs** - Step IDs are unique within the expert (across all workflows)
+7. **Valid Dependencies** - Dependencies reference existing step IDs with no circular references
+8. **Input Consistency** - File inputs with `from_step` reference a step in the dependencies array
+9. **Output Paths** - Outputs are valid filenames or paths
+
+For each criterion, determine PASS or FAIL. If FAIL, note the specific issue and fix.
+
+### Step 3: Fix Failed Criteria
+
+For each failed criterion, edit the workflow.yml:
+
+| Criterion | Common Issue | Fix |
+|-----------|-------------|-----|
+| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
+| Semantic Version | Invalid format | Set to `"1.0.0"` |
+| Concise Summary | Too long | Shorten to <200 chars |
+| Rich Description | Single line | Add multi-line explanation |
+| Complete Steps | Missing fields | Add required fields |
+| Unique Step IDs | Duplicate ID | Rename to unique identifier |
+| Valid Dependencies | Non-existent step | Fix step ID reference |
+| Input Consistency | from_step not in deps | Add step to dependencies |
+| Output Paths | Invalid format | Use valid filename/path |
+
+### Step 4: Re-Evaluate (If Needed)
+
+If any criteria failed:
+1. Review the updated workflow.yml
+2. Re-evaluate all criteria
+3. Fix remaining issues
+4. Repeat until all pass
+
+### Step 5: Confirm Completion
+
+When all criteria pass:
+
+1. Announce: "All workflow spec quality criteria pass."
+2. List what was validated
+3. Guide to next step: "Run `/experts.implement` to generate the step instruction files."
+
+## Output
+
+The validated `workflow.yml` file at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml` that passes all quality criteria.
diff --git a/.deepwork/experts/experts/workflows/new_workflow/workflow.yml b/.deepwork/experts/experts/workflows/new_workflow/workflow.yml
new file mode 100644
index 00000000..0121ba2a
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/new_workflow/workflow.yml
@@ -0,0 +1,58 @@
+name: new_workflow
+version: "1.0.0"
+summary: "Create a new multi-step DeepWork workflow with spec definition, review, and implementation"
+
+description: |
+ Guide the user through creating a new DeepWork workflow by:
+ 1. Understanding their workflow requirements through interactive questioning
+ 2. Creating a validated workflow.yml specification
+ 3. Reviewing the spec against quality criteria
+ 4. Generating step instruction files and syncing skills
+
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Create a workflow.yml by gathering requirements through structured questions"
+ instructions_file: steps/define.md
+ inputs:
+ - name: workflow_purpose
+ description: "What complex task are you trying to accomplish?"
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ exposed: true
+
+ - id: review_workflow_spec
+ name: "Review Workflow Specification"
+ description: "Review workflow.yml against quality criteria using a sub-agent for unbiased validation"
+ instructions_file: steps/review_workflow_spec.md
+ inputs:
+ - file: ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ from_step: define
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ dependencies:
+ - define
+ agent: general-purpose
+ quality_criteria:
+ - "Workflow name is lowercase with underscores, no spaces or special characters"
+ - "Version follows X.Y.Z format (e.g., 1.0.0)"
+ - "Summary is under 200 characters and clearly describes the workflow"
+ - "Description is multi-line and explains problem, process, outcomes, and users"
+ - "Each step has id, name, description, instructions_file, outputs"
+ - "Step IDs are unique within the expert (across all workflows)"
+ - "Dependencies reference existing step IDs with no circular references"
+ - "File inputs with from_step reference a step in the dependencies array"
+ - "Outputs are valid filenames or paths"
+
+ - id: implement
+ name: "Implement Workflow Steps"
+ description: "Generate step instruction files and sync skills from the validated workflow.yml"
+ instructions_file: steps/implement.md
+ inputs:
+ - file: ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ from_step: review_workflow_spec
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/"
+ dependencies:
+ - review_workflow_spec
+ exposed: true
diff --git a/.deepwork/experts/experts/workflows/review_pr/steps/check_relevance.md b/.deepwork/experts/experts/workflows/review_pr/steps/check_relevance.md
new file mode 100644
index 00000000..d9dea491
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/review_pr/steps/check_relevance.md
@@ -0,0 +1,121 @@
+# Check Expert Relevance
+
+## Objective
+
+Invoke all available experts in parallel to determine which ones have relevant expertise for the PR changes. This filtering step ensures only applicable experts spend time on detailed review.
+
+## Task
+
+Assess which domain experts can meaningfully contribute to reviewing the current PR by having each expert examine the changes and report their relevance.
+
+### Process
+
+1. **Get PR metadata**
+
+ Get basic PR info for the output file:
+ ```bash
+ gh pr view --json number,title,headRefName
+ ```
+
+ This is needed for the output file header. The diff and file list will be
+ embedded directly in expert prompts using inline command expansion.
+
+2. **Discover available experts**
+
+ List all experts in the project:
+ ```bash
+ ls -1 .deepwork/experts/
+ ```
+
+ Read each expert's `discovery_description` from their `expert.yml` to understand their domain.
+
+3. **Invoke experts in parallel**
+
+ For each expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Use `$(command)` syntax in the prompt to embed command output directly when
+ the sub-agent is spawned. This avoids reading large diffs into the main
+ conversation context.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Review this PR to determine if the changes are relevant to your domain of expertise.
+
+ ## Changed Files
+
+ $(gh pr diff --name-only)
+
+ ## Diff Summary (first 200 lines)
+
+ $(gh pr diff | head -200)
+
+ ## Your Task
+
+ Based on your specific domain knowledge, respond with:
+ 1. RELEVANT or NOT_RELEVANT
+ 2. Brief justification (1-2 sentences) explaining why this does or does not fall within your expertise
+ 3. If relevant, which specific files/changes you can review from your expert perspective
+ ```
+
+ **Important**: Invoke ALL experts in parallel to minimize latency.
+
+4. **Collect and summarize responses**
+
+ Wait for all expert responses. Compile into the output file.
+
+## Output Format
+
+### pr_review/relevance_assessments.md
+
+Create this file with the assessment results:
+
+```markdown
+# PR Relevance Assessment
+
+**PR**: #[number] - [title]
+**Branch**: [branch_name]
+**Date**: [YYYY-MM-DD]
+
+## Changed Files
+
+- path/to/file1.py
+- path/to/file2.ts
+- ...
+
+## Expert Assessments
+
+### [expert-name-1]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+### [expert-name-2]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+...
+
+## Summary
+
+**Relevant experts**: [list of expert names marked RELEVANT]
+**Next step**: Run `/experts.deep_review` with these experts
+```
+
+## Quality Criteria
+
+- All experts in `.deepwork/experts/` were invoked in parallel
+- Each expert provided a yes/no relevance determination with justification
+- Relevant experts are clearly identified for the next step
+- PR metadata (number, title, branch) is captured
+- Changed files are listed for reference
+
+## Context
+
+This is the first phase of the expert-driven PR review workflow. By checking relevance first, we avoid wasting expert review time on changes outside their domain. The parallel invocation ensures this filtering step completes quickly even with many experts.
+
+The output file serves as input to the `deep_review` step, which will only invoke experts marked as RELEVANT.
diff --git a/.deepwork/experts/experts/workflows/review_pr/steps/deep_review.md b/.deepwork/experts/experts/workflows/review_pr/steps/deep_review.md
new file mode 100644
index 00000000..01544707
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/review_pr/steps/deep_review.md
@@ -0,0 +1,192 @@
+# Deep Expert Review
+
+## Objective
+
+Have relevant domain experts perform thorough code review of the PR changes **specifically within their area of expertise**. Each expert produces detailed written feedback with specific suggestions for improvement, drawing on their domain knowledge.
+
+## Task
+
+Invoke only the experts marked as RELEVANT in the previous step to perform detailed code review, producing actionable feedback focused on their specific domain.
+
+### Process
+
+1. **Read the relevance assessments**
+
+ Read `pr_review/relevance_assessments.md` to identify:
+ - Which experts were marked RELEVANT
+ - Which specific files each expert identified as relevant to their domain
+ - The PR number and branch
+
+2. **Invoke relevant experts for deep review**
+
+ For each RELEVANT expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Instead of reading the diff yourself and passing it to each expert, use `$(command)`
+ syntax in the prompt. This embeds the command output directly when the sub-agent
+ is spawned, avoiding token overhead in the main conversation.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Perform a detailed code review of these PR changes **focusing specifically on your domain of expertise**.
+
+ IMPORTANT: Only comment on aspects that fall within your area of expertise. Do not provide
+ general code review feedback on things outside your domain - other experts will cover those areas.
+ Use your specialized knowledge to identify issues that a generalist reviewer might miss.
+
+ Your domain: [brief description from discovery_description]
+
+ Files you identified as relevant to your domain:
+ [list from relevance assessment]
+
+ ## PR Diff
+
+ $(gh pr diff)
+
+ ## Your Task
+
+ Review the diff above, focusing ONLY on files and changes relevant to your domain.
+ Ignore changes outside your expertise - other experts will cover those.
+
+ From your expert perspective, provide your review in this format:
+
+ ## Summary
+ Overall assessment of the changes as they relate to your domain (1-2 paragraphs).
+ What is this PR doing in terms of your area of expertise?
+
+ ## Issues Found
+ For each issue within your domain:
+ - **File**: path/to/file
+ - **Line(s)**: [line numbers]
+ - **Severity**: Critical / Major / Minor / Suggestion
+ - **Issue**: Description of the problem from your expert perspective
+ - **Suggestion**: How to fix it, drawing on your domain knowledge
+
+ ## Code Suggestions
+ Specific code changes you recommend based on your expertise (include before/after snippets).
+ Explain why this change is better from your domain's perspective.
+
+ ## Approval Status
+ - APPROVED: No blocking issues within your domain
+ - CHANGES_REQUESTED: Blocking issues in your domain must be addressed
+ - COMMENTS: Suggestions only within your domain, no blockers
+ ```
+
+ **Note**: Invoke relevant experts in parallel for efficiency.
+
+3. **Collect expert reviews**
+
+ Wait for all expert reviews to complete. Save each to their dedicated review file.
+
+4. **Create consolidated summary**
+
+ After all reviews complete, summarize across experts:
+ - Total issues by severity
+ - Key themes across reviews
+ - Overall approval status
+
+## Output Format
+
+### pr_review/[expert_name]/review.md
+
+Create one file per relevant expert:
+
+```markdown
+# [Expert Name] Review
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+**Reviewer**: [expert-name] expert
+
+## Summary
+
+[Expert's overall assessment from their domain perspective]
+
+## Issues Found
+
+### Issue 1
+- **File**: path/to/file.py
+- **Line(s)**: 45-52
+- **Severity**: Major
+- **Issue**: [Description from expert perspective]
+- **Suggestion**: [How to fix, using domain knowledge]
+
+### Issue 2
+...
+
+## Code Suggestions
+
+### Suggestion 1: [Brief title]
+
+**File**: path/to/file.py
+
+Before:
+```python
+# problematic code
+```
+
+After:
+```python
+# suggested improvement
+```
+
+**Rationale**: [Why this change improves the code from this expert's perspective]
+
+...
+
+## Approval Status
+
+[APPROVED / CHANGES_REQUESTED / COMMENTS]
+
+[Additional notes on approval status]
+```
+
+### pr_review/review_summary.md (optional but recommended)
+
+```markdown
+# PR Review Summary
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+
+## Expert Reviews
+
+| Expert | Domain | Status | Critical | Major | Minor |
+|--------|--------|--------|----------|-------|-------|
+| [name] | [domain] | CHANGES_REQUESTED | 0 | 2 | 3 |
+| [name] | [domain] | APPROVED | 0 | 0 | 1 |
+
+## Key Themes
+
+- [Theme 1 appearing across multiple reviews]
+- [Theme 2]
+
+## Overall Status
+
+[CHANGES_REQUESTED if any expert requested changes, otherwise APPROVED]
+
+## Next Steps
+
+[If changes requested]: Run `/experts.improve_and_rereview` to address feedback
+[If approved]: PR is ready to merge
+```
+
+## Quality Criteria
+
+- Only experts marked as relevant were invoked
+- Each expert produced written feedback in their review file
+- Feedback is focused on each expert's specific domain of expertise
+- Specific code change suggestions are included where applicable
+- Issues include file, line numbers, severity, and suggestions
+- Approval status is clearly stated by each expert
+
+## Context
+
+This is the second phase of the expert-driven PR review workflow. Deep review only happens after relevance filtering to ensure expert time is well-spent. Each expert reviews independently from their unique domain perspective, bringing specialized knowledge to identify issues that generalist reviewers might miss.
+
+The key to effective expert review is **focus**: each expert should only comment on aspects within their domain, trusting that other experts will cover other areas. This produces higher-quality, more actionable feedback than generic reviews.
+
+The review files serve as input to the `improve_and_rereview` step if changes are requested. If all experts approve, the PR can proceed to merge.
diff --git a/.deepwork/experts/experts/workflows/review_pr/steps/improve_and_rereview.md b/.deepwork/experts/experts/workflows/review_pr/steps/improve_and_rereview.md
new file mode 100644
index 00000000..7218e174
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/review_pr/steps/improve_and_rereview.md
@@ -0,0 +1,226 @@
+# Improve and Re-Review
+
+## Objective
+
+Iteratively improve the PR based on expert feedback, then re-run expert reviews until all feedback is addressed or a maximum of 3 iterations is reached.
+
+## Task
+
+Apply improvements based on expert review feedback, get user approval, then have the same experts re-review. Repeat until experts report no further issues.
+
+### Process
+
+1. **Read expert reviews**
+
+ Read all review files from `pr_review/[expert_name]/review.md` to understand:
+ - What issues each expert identified within their domain
+ - What code changes they suggested
+ - Their approval status
+
+2. **Consolidate and prioritize feedback**
+
+ Group feedback by:
+ - **Critical/Major issues**: Must be addressed
+ - **Minor issues**: Should be addressed
+ - **Suggestions**: Nice to have
+
+ Present a summary to the user:
+ ```
+ ## Feedback Summary
+
+ ### Critical/Major Issues (must fix)
+ 1. [Issue from expert X]: [description]
+ 2. [Issue from expert Y]: [description]
+
+ ### Minor Issues (should fix)
+ 1. [Issue]: [description]
+
+ ### Suggestions (optional)
+ 1. [Suggestion]: [description]
+
+ ## Proposed Changes
+
+ I recommend making these changes:
+ 1. [Change 1]: [what and why]
+ 2. [Change 2]: [what and why]
+
+ Do you approve these changes?
+ ```
+
+3. **Get user approval**
+
+ **IMPORTANT**: Do not make changes without explicit user approval.
+
+ Wait for user to:
+ - Approve the proposed changes
+ - Modify which changes to apply
+ - Skip certain suggestions with justification
+
+4. **Apply approved changes**
+
+ Make the code changes the user approved:
+ - Use the Edit tool to modify files
+ - Follow the expert's code suggestions where applicable
+ - Ensure changes are coherent and don't break other code
+
+5. **Re-run expert reviews**
+
+ After applying changes, invoke the same relevant experts again:
+ - Use the Task tool with `subagent_type`: "experts"
+ - Provide the updated file contents and diff
+ - Ask them to re-review from their domain perspective
+
+ **Expert re-review prompt**:
+ ```
+ This is a re-review of PR changes after addressing your previous feedback.
+
+ Your domain: [brief description from discovery_description]
+
+ Previous issues you raised within your domain:
+ [list their issues from last review]
+
+ Changes made:
+ [summary of changes applied]
+
+ Updated files:
+ [full file contents]
+
+ From your expert perspective, please review and report:
+ 1. Are your previous domain-specific issues addressed?
+ 2. Any new issues introduced within your domain?
+ 3. Updated approval status for your domain
+ ```
+
+6. **Evaluate results**
+
+ After re-review:
+ - If ALL experts now approve (or only have minor suggestions): **Stop - review complete**
+ - If any expert still has Critical/Major issues: **Continue to next iteration**
+ - If this was iteration 3: **Stop - maximum iterations reached**
+
+7. **Create iteration summary**
+
+ Document what happened in this iteration.
+
+### Iteration Loop
+
+Repeat steps 2-7 until:
+- All experts report no further blocking feedback, OR
+- 3 iterations have been completed
+
+## Output Format
+
+### pr_review/iteration_[n]/summary.md
+
+Create one file per iteration:
+
+```markdown
+# Iteration [n] Summary
+
+**Date**: [YYYY-MM-DD]
+**Iteration**: [n] of 3 max
+
+## Feedback Addressed
+
+**IMPORTANT**: List ALL issues from expert reviews - none may be silently omitted.
+
+### From [expert-name]
+| Issue | Severity | Status | Resolution |
+|-------|----------|--------|------------|
+| [Issue 1] | Major | FIXED | [How it was fixed] |
+| [Issue 2] | Minor | SKIPPED | [Explicit reason: false positive / deferred / user declined] |
+| [Issue 3] | Suggestion | FIXED | [How it was fixed] |
+
+### From [expert-name-2]
+...
+
+## Changes Made
+
+1. **[file.py]**: [What was changed]
+2. **[other.ts]**: [What was changed]
+
+## Re-Review Results
+
+| Expert | Previous Status | New Status | Remaining Issues |
+|--------|-----------------|------------|------------------|
+| [name] | CHANGES_REQUESTED | APPROVED | 0 |
+| [name] | CHANGES_REQUESTED | COMMENTS | 1 minor |
+
+## Outcome
+
+[One of:]
+- **COMPLETE**: All experts approved. PR ready to merge.
+- **CONTINUING**: [n] blocking issues remain. Proceeding to iteration [n+1].
+- **MAX_ITERATIONS**: Reached 3 iterations. [n] issues remain unresolved.
+
+## Remaining Issues (if any)
+
+1. [Issue]: [Why not addressed / Plan for addressing]
+```
+
+### Final Summary (after last iteration)
+
+Update or create `pr_review/final_summary.md`:
+
+```markdown
+# PR Review Final Summary
+
+**PR**: #[number]
+**Total Iterations**: [n]
+**Final Status**: [APPROVED / PARTIAL / UNRESOLVED]
+
+## Review Timeline
+
+| Iteration | Date | Issues Addressed | Remaining |
+|-----------|------|------------------|-----------|
+| 1 | [date] | 5 | 3 |
+| 2 | [date] | 3 | 0 |
+
+## Expert Final Status
+
+| Expert | Final Status |
+|--------|--------------|
+| [name] | APPROVED |
+| [name] | APPROVED |
+
+## Key Improvements Made
+
+1. [Improvement 1]
+2. [Improvement 2]
+
+## Issue Resolution Summary
+
+| Expert | Total Issues | Fixed | Skipped (with reason) |
+|--------|--------------|-------|----------------------|
+| [name] | 4 | 3 | 1 (false positive) |
+| [name] | 2 | 2 | 0 |
+
+## Unresolved Items (if any)
+
+For each unresolved item, document:
+- Issue description
+- Why it wasn't fixed
+- Plan for addressing (if applicable)
+
+## Next Steps
+
+[What to do with the PR now]
+```
+
+## Quality Criteria
+
+- **Complete issue accounting**: Every issue from expert reviews is either:
+ - Fixed (with description of the fix), OR
+ - Explicitly skipped with documented justification (e.g., "false positive", "deferred to future PR", "user declined")
+- **No silent omissions**: The feedback summary must list ALL issues from expert reviews, not a subset
+- User approved suggested improvements before they were applied
+- Re-reviews were run after each improvement iteration
+- Cycle stopped when all experts reported no further feedback OR after 3 iterations
+- Final iteration summary documents the outcome for every issue
+- All changes are tracked and attributed to expert feedback
+
+## Context
+
+This is the final phase of the expert-driven PR review workflow. The iterative approach ensures feedback is actually addressed, not just acknowledged. The 3-iteration limit prevents infinite loops while allowing reasonable time for improvements.
+
+User approval at each step keeps humans in control - experts suggest, but humans decide what changes to make. This respects developer autonomy while benefiting from expert review.
diff --git a/.deepwork/experts/experts/workflows/review_pr/workflow.yml b/.deepwork/experts/experts/workflows/review_pr/workflow.yml
new file mode 100644
index 00000000..2c5455da
--- /dev/null
+++ b/.deepwork/experts/experts/workflows/review_pr/workflow.yml
@@ -0,0 +1,44 @@
+name: review_pr
+version: "1.0.0"
+summary: "Coordinate expert-driven PR review with iterative improvement cycles"
+
+description: |
+ Orchestrate a comprehensive PR review using domain experts:
+ 1. Check which experts have relevant expertise for the PR changes
+ 2. Have relevant experts perform deep code review
+ 3. Iteratively improve the PR based on feedback until all experts approve
+
+steps:
+ - id: check_relevance
+ name: "Check Expert Relevance"
+ description: "Invoke all experts in parallel to assess if PR changes are relevant to their domain"
+ instructions_file: steps/check_relevance.md
+ outputs:
+ - "pr_review/relevance_assessments.md"
+ exposed: true
+
+ - id: deep_review
+ name: "Deep Expert Review"
+ description: "Invoke only relevant experts to perform detailed code review"
+ instructions_file: steps/deep_review.md
+ inputs:
+ - file: "pr_review/relevance_assessments.md"
+ from_step: check_relevance
+ outputs:
+ - "pr_review/{expert_name}/review.md"
+ - "pr_review/review_summary.md"
+ dependencies:
+ - check_relevance
+
+ - id: improve_and_rereview
+ name: "Improve and Re-Review"
+ description: "Apply expert feedback and re-run reviews until no further feedback or 3 iterations"
+ instructions_file: steps/improve_and_rereview.md
+ inputs:
+ - file: "pr_review/{expert_name}/review.md"
+ from_step: deep_review
+ outputs:
+ - "pr_review/iteration_{n}/summary.md"
+ - "pr_review/final_summary.md"
+ dependencies:
+ - deep_review
diff --git a/.deepwork/jobs/deepwork_jobs/AGENTS.md b/.deepwork/jobs/deepwork_jobs/AGENTS.md
deleted file mode 100644
index 6d97d0e5..00000000
--- a/.deepwork/jobs/deepwork_jobs/AGENTS.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Project Context for deepwork_jobs
-
-This is the source of truth for the `deepwork_jobs` standard job.
-
-## Codebase Structure
-
-- Source location: `src/deepwork/standard_jobs/deepwork_jobs/`
-- Working copy: `.deepwork/jobs/deepwork_jobs/`
-- Templates: `templates/` directory within each location
-
-## Dual Location Maintenance
-
-**Important**: This job exists in two locations that must be kept in sync:
-
-1. **Source of truth**: `src/deepwork/standard_jobs/deepwork_jobs/`
- - This is where changes should be made first
- - Tracked in version control
-
-2. **Working copy**: `.deepwork/jobs/deepwork_jobs/`
- - Must be updated after changes to source
- - Used by `deepwork sync` to generate commands
-
-After making changes to the source, copy files to the working copy:
-```bash
-cp src/deepwork/standard_jobs/deepwork_jobs/job.yml .deepwork/jobs/deepwork_jobs/
-cp src/deepwork/standard_jobs/deepwork_jobs/steps/*.md .deepwork/jobs/deepwork_jobs/steps/
-cp -r src/deepwork/standard_jobs/deepwork_jobs/templates/* .deepwork/jobs/deepwork_jobs/templates/
-```
-
-## File Organization
-
-```
-deepwork_jobs/
-├── AGENTS.md # This file
-├── job.yml # Job definition
-├── make_new_job.sh # Script to create new job structure
-├── steps/
-│ ├── define.md # Define step instructions
-│ ├── implement.md # Implement step instructions
-│ ├── learn.md # Learn step instructions
-│ └── supplemental_file_references.md # Reference documentation
-└── templates/
- ├── job.yml.template # Job spec structure
- ├── step_instruction.md.template # Step instruction structure
- ├── agents.md.template # AGENTS.md structure
- ├── job.yml.example # Complete job example
- └── step_instruction.md.example # Complete step example
-```
-
-## Version Management
-
-- Version is tracked in `job.yml`
-- Bump patch version (0.0.x) for instruction improvements
-- Bump minor version (0.x.0) for new features or structural changes
-- Always update changelog when bumping version
-
-## Last Updated
-
-- Date: 2026-01-15
-- From conversation about: Adding make_new_job.sh script and templates directory
diff --git a/.deepwork/jobs/deepwork_jobs/doc_specs/job_spec.md b/.deepwork/jobs/deepwork_jobs/doc_specs/job_spec.md
deleted file mode 100644
index b880bb17..00000000
--- a/.deepwork/jobs/deepwork_jobs/doc_specs/job_spec.md
+++ /dev/null
@@ -1,203 +0,0 @@
----
-name: "DeepWork Job Specification"
-description: "YAML specification file that defines a multi-step workflow job for AI agents"
-path_patterns:
- - ".deepwork/jobs/*/job.yml"
-target_audience: "AI agents executing jobs and developers defining workflows"
-frequency: "Created once per job, updated as workflow evolves"
-quality_criteria:
- - name: Valid Identifier
- description: "Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)"
- - name: Semantic Version
- description: "Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)"
- - name: Concise Summary
- description: "Summary must be under 200 characters and clearly describe what the job accomplishes"
- - name: Rich Description
- description: "Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users"
- - name: Changelog Present
- description: "Must include a changelog array with at least the initial version entry. Changelog should only include one entry per branch at most"
- - name: Complete Steps
- description: "Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array"
- - name: Valid Dependencies
- description: "Dependencies must reference existing step IDs with no circular references"
- - name: Input Consistency
- description: "File inputs with `from_step` must reference a step that is in the dependencies array"
- - name: Output Paths
- description: "Outputs must be valid filenames or paths within the main repo directory structure, never in dot-directories like `.deepwork/`. Use specific, descriptive paths that lend themselves to glob patterns (e.g., `competitive_research/acme_corp/swot.md` or `operations/reports/2026-01/spending_analysis.md`). Parameterized paths like `[competitor_name]/` are encouraged for per-entity outputs. Avoid generic names (`output.md`, `analysis.md`) and transient-sounding paths (`temp/`, `draft.md`). Supporting materials for a final output should go in a peer `_dataroom` folder (e.g., `spending_analysis_dataroom/`)."
- - name: Concise Instructions
- description: "The content of the file, particularly the description, must not have excessively redundant information. It should be concise and to the point given that extra tokens will confuse the AI."
----
-
-# DeepWork Job Specification: [job_name]
-
-A `job.yml` file defines a complete multi-step workflow that AI agents can execute. Each job breaks down a complex task into reviewable steps with clear inputs and outputs.
-
-## Required Fields
-
-### Top-Level Metadata
-
-```yaml
-name: job_name # lowercase, underscores only
-version: "1.0.0" # semantic versioning
-summary: "Brief description" # max 200 characters
-description: | # detailed multi-line explanation
- [Explain what this workflow does, why it exists,
- what outputs it produces, and who should use it]
-```
-
-### Changelog
-
-```yaml
-changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
- - version: "1.1.0"
- changes: "Added quality validation hooks"
-```
-
-### Steps Array
-
-```yaml
-steps:
- - id: step_id # unique, lowercase_underscores
- name: "Human Readable Name"
- description: "What this step accomplishes"
- instructions_file: steps/step_id.md
- inputs:
- # User-provided inputs:
- - name: param_name
- description: "What the user provides"
- # File inputs from previous steps:
- - file: output.md
- from_step: previous_step_id
- outputs:
- - competitive_research/competitors_list.md # descriptive path
- - competitive_research/[competitor_name]/research.md # parameterized path
- # With doc spec reference:
- - file: competitive_research/final_report.md
- doc_spec: .deepwork/doc_specs/report_type.md
- dependencies:
- - previous_step_id # steps that must complete first
-```
-
-## Optional Fields
-
-### Exposed Steps
-
-```yaml
-steps:
- - id: learn
- exposed: true # Makes step available without running dependencies
-```
-
-### Agent Delegation
-
-When a step should be executed by a specific agent type, use the `agent` field. This automatically sets `context: fork` in the generated skill.
-
-```yaml
-steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
-```
-
-Available agent types:
-- `general-purpose` - Standard agent for multi-step tasks
-
-### Quality Hooks
-
-```yaml
-steps:
- - id: step_id
- hooks:
- after_agent:
- # Inline prompt for quality validation:
- - prompt: |
- Verify the output meets criteria:
- 1. [Criterion 1]
- 2. [Criterion 2]
- If ALL criteria are met, include `...`.
- # External prompt file:
- - prompt_file: hooks/quality_check.md
- # Script for programmatic validation:
- - script: hooks/run_tests.sh
-```
-
-### Stop Hooks (Legacy)
-
-```yaml
-steps:
- - id: step_id
- stop_hooks:
- - prompt: "Validation prompt..."
- - prompt_file: hooks/check.md
- - script: hooks/validate.sh
-```
-
-## Validation Rules
-
-1. **No circular dependencies**: Step A cannot depend on Step B if Step B depends on Step A
-2. **File inputs require dependencies**: If a step uses `from_step: X`, then X must be in its dependencies
-3. **Unique step IDs**: No two steps can have the same id
-4. **Valid file paths**: Output paths must not contain invalid characters and should be in the main repo (not dot-directories)
-5. **Instructions files exist**: Each `instructions_file` path should have a corresponding file created
-
-## Example: Complete Job Specification
-
-```yaml
-name: competitive_research
-version: "1.0.0"
-summary: "Systematic competitive analysis workflow"
-description: |
- A comprehensive workflow for analyzing competitors in your market segment.
- Helps product teams understand the competitive landscape through systematic
- identification, research, comparison, and positioning recommendations.
-
- Produces:
- - Vetted competitor list
- - Research notes per competitor
- - Comparison matrix
- - Strategic positioning report
-
-changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
-
-steps:
- - id: identify_competitors
- name: "Identify Competitors"
- description: "Identify 5-7 key competitors in the target market"
- instructions_file: steps/identify_competitors.md
- inputs:
- - name: market_segment
- description: "The market segment to analyze"
- - name: product_category
- description: "The product category"
- outputs:
- - competitive_research/competitors_list.md
- dependencies: []
-
- - id: research_competitors
- name: "Research Competitors"
- description: "Deep dive research on each identified competitor"
- instructions_file: steps/research_competitors.md
- inputs:
- - file: competitive_research/competitors_list.md
- from_step: identify_competitors
- outputs:
- - competitive_research/[competitor_name]/research.md
- dependencies:
- - identify_competitors
-
- - id: positioning_report
- name: "Positioning Report"
- description: "Strategic positioning recommendations"
- instructions_file: steps/positioning_report.md
- inputs:
- - file: competitive_research/[competitor_name]/research.md
- from_step: research_competitors
- outputs:
- - file: competitive_research/positioning_report.md
- doc_spec: .deepwork/doc_specs/positioning_report.md
- dependencies:
- - research_competitors
-```
diff --git a/.deepwork/jobs/deepwork_jobs/job.yml b/.deepwork/jobs/deepwork_jobs/job.yml
deleted file mode 100644
index 4343cbda..00000000
--- a/.deepwork/jobs/deepwork_jobs/job.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-name: deepwork_jobs
-version: "1.0.0"
-summary: "Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs."
-description: |
- Core commands for managing DeepWork jobs. These commands help you define new multi-step
- workflows and learn from running them.
-
- The `new_job` workflow guides you through defining and implementing a new job by
- asking structured questions about your workflow, understanding each step's inputs and outputs,
- reviewing the specification, and generating all necessary files.
-
- The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
- confusion or inefficiencies, and improves job instructions. It also captures bespoke
- learnings specific to the current run into AGENTS.md files in the working folder.
-
-workflows:
- - name: new_job
- summary: "Create a new DeepWork job from scratch through definition, review, and implementation"
- steps:
- - define
- - review_job_spec
- - implement
-
-changelog:
- - version: "1.0.0"
- changes: "Added workflows section to distinguish new_job workflow (define→review_job_spec→implement) from standalone learn skill"
- - version: "0.1.0"
- changes: "Initial version"
- - version: "0.2.0"
- changes: "Replaced refine command with learn command for conversation-driven improvement"
- - version: "0.3.0"
- changes: "Added make_new_job.sh script and templates directory; updated instructions to reference templates instead of inline examples"
- - version: "0.4.0"
- changes: "Removed implementation_summary and learning_summary outputs; simplified step outputs"
- - version: "0.5.0"
- changes: "Standardized on 'ask structured questions' phrasing for user input; Updated quality criteria hooks to verify phrase usage; Added guidance in implement.md to use phrase in generated instructions"
- - version: "0.6.0"
- changes: "Added doc spec support; define.md now detects document-oriented workflows and guides doc spec creation; learn.md now identifies and applies doc spec-related improvements"
- - version: "0.7.0"
- changes: "Added job.yml doc spec; define step now outputs job.yml with doc_spec reference for quality validation"
- - version: "0.8.0"
- changes: "Added review_job_spec step between define and implement for doc spec-based quality validation using sub-agent review"
- - version: "0.9.0"
- changes: "Improved skill descriptions with third-person voice and 'Use when...' triggers for better discoverability"
-
-steps:
- - id: define
- name: "Define Job Specification"
- description: "Creates a job.yml specification by gathering workflow requirements through structured questions. Use when starting a new multi-step workflow."
- instructions_file: steps/define.md
- inputs:
- - name: job_purpose
- description: "What complex task or workflow are you trying to accomplish?"
- outputs:
- - file: job.yml
- doc_spec: .deepwork/doc_specs/job_spec.md
- dependencies: []
- - id: review_job_spec
- name: "Review Job Specification"
- description: "Reviews job.yml against quality criteria using a sub-agent for unbiased validation. Use after defining a job specification."
- instructions_file: steps/review_job_spec.md
- inputs:
- - file: job.yml
- from_step: define
- outputs:
- - file: job.yml
- doc_spec: .deepwork/doc_specs/job_spec.md
- dependencies:
- - define
- quality_criteria:
- - "**Sub-Agent Used**: Was a sub-agent spawned to provide unbiased review?"
- - "**All doc spec Criteria Evaluated**: Did the sub-agent assess all 9 quality criteria?"
- - "**Findings Addressed**: Were all failed criteria addressed by the main agent?"
- - "**Validation Loop Complete**: Did the review-fix cycle continue until all criteria passed?"
-
- - id: implement
- name: "Implement Job Steps"
- description: "Generates step instruction files and syncs slash commands from the job.yml specification. Use after job spec review passes."
- instructions_file: steps/implement.md
- inputs:
- - file: job.yml
- from_step: review_job_spec
- outputs:
- - steps/
- dependencies:
- - review_job_spec
- quality_criteria:
- - "**Directory Structure**: Is `.deepwork/jobs/[job_name]/` created correctly?"
- - "**Complete Instructions**: Are ALL step instruction files complete (not stubs or placeholders)?"
- - "**Specific & Actionable**: Are instructions tailored to each step's purpose, not generic?"
- - "**Output Examples**: Does each instruction file show what good output looks like?"
- - "**Quality Criteria**: Does each instruction file define quality criteria for its outputs?"
- - "**Ask Structured Questions**: Do step instructions that gather user input explicitly use the phrase \"ask structured questions\"?"
- - "**Sync Complete**: Has `deepwork sync` been run successfully?"
- - "**Commands Available**: Are the slash-commands generated in `.claude/commands/`?"
- - "**Rules Considered**: Has the agent thought about whether rules would benefit this job? If relevant rules were identified, did they explain them and offer to run `/deepwork_rules.define`? Not every job needs rules - only suggest when genuinely helpful."
-
- - id: learn
- name: "Learn from Job Execution"
- description: "Analyzes conversation history to improve job instructions and capture learnings. Use after running a job to refine it."
- instructions_file: steps/learn.md
- exposed: true
- inputs:
- - name: job_name
- description: "Name of the job that was run (optional - will auto-detect from conversation)"
- outputs:
- - AGENTS.md
- dependencies: []
- quality_criteria:
- - "**Conversation Analyzed**: Did the agent review the conversation for DeepWork job executions?"
- - "**Confusion Identified**: Did the agent identify points of confusion, errors, or inefficiencies?"
- - "**Instructions Improved**: Were job instructions updated to address identified issues?"
- - "**Instructions Concise**: Are instructions free of redundancy and unnecessary verbosity?"
- - "**Shared Content Extracted**: Is lengthy/duplicated content extracted into referenced files?"
- - "**doc spec Reviewed (if applicable)**: For jobs with doc spec outputs, were doc spec-related learnings identified?"
- - "**doc spec Updated (if applicable)**: Were doc spec files updated with improved quality criteria or structure?"
- - "**Bespoke Learnings Captured**: Were run-specific learnings added to AGENTS.md?"
- - "**File References Used**: Do AGENTS.md entries reference other files where appropriate?"
- - "**Working Folder Correct**: Is AGENTS.md in the correct working folder for the job?"
- - "**Generalizable Separated**: Are generalizable improvements in instructions, not AGENTS.md?"
- - "**Sync Complete**: Has `deepwork sync` been run if instructions were modified?"
diff --git a/.deepwork/jobs/deepwork_jobs/make_new_job.sh b/.deepwork/jobs/deepwork_jobs/make_new_job.sh
deleted file mode 100755
index c561d6d2..00000000
--- a/.deepwork/jobs/deepwork_jobs/make_new_job.sh
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env bash
-#
-# make_new_job.sh - Create directory structure for a new DeepWork job
-#
-# Usage: ./make_new_job.sh
-#
-
-set -euo pipefail
-
-# Color output helpers
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-NC='\033[0m' # No Color
-
-info() {
- echo -e "${GREEN}[INFO]${NC} $1"
-}
-
-warn() {
- echo -e "${YELLOW}[WARN]${NC} $1"
-}
-
-error() {
- echo -e "${RED}[ERROR]${NC} $1"
- exit 1
-}
-
-# Validate job name format
-validate_job_name() {
- local name="$1"
- if [[ ! "$name" =~ ^[a-z][a-z0-9_]*$ ]]; then
- error "Invalid job name '$name'. Must be lowercase, start with a letter, and contain only letters, numbers, and underscores."
- fi
-}
-
-# Main script
-main() {
- if [[ $# -lt 1 ]]; then
- echo "Usage: $0 "
- echo ""
- echo "Creates the directory structure for a new DeepWork job."
- echo ""
- echo "Arguments:"
- echo " job_name Name of the job (lowercase, underscores allowed)"
- echo ""
- echo "Example:"
- echo " $0 competitive_research"
- exit 1
- fi
-
- local job_name="$1"
- validate_job_name "$job_name"
-
- # Determine the base path - look for .deepwork directory
- local base_path
- if [[ -d ".deepwork/jobs" ]]; then
- base_path=".deepwork/jobs"
- elif [[ -d "../.deepwork/jobs" ]]; then
- base_path="../.deepwork/jobs"
- else
- # Create from current directory
- base_path=".deepwork/jobs"
- mkdir -p "$base_path"
- fi
-
- local job_path="${base_path}/${job_name}"
-
- # Check if job already exists
- if [[ -d "$job_path" ]]; then
- error "Job '$job_name' already exists at $job_path"
- fi
-
- info "Creating job directory structure for '$job_name'..."
-
- # Create main job directory and subdirectories
- mkdir -p "$job_path"
- mkdir -p "$job_path/steps"
- mkdir -p "$job_path/hooks"
- mkdir -p "$job_path/templates"
-
- # Add .gitkeep files to empty directories
- touch "$job_path/hooks/.gitkeep"
- touch "$job_path/templates/.gitkeep"
-
- # Create AGENTS.md file
- cat > "$job_path/AGENTS.md" << 'EOF'
-# Job Management
-
-This folder and its subfolders are managed using the `deepwork_jobs` slash commands.
-
-## Recommended Commands
-
-- `/deepwork_jobs.define` - Create or modify the job.yml specification
-- `/deepwork_jobs.implement` - Generate step instruction files from the specification
-- `/deepwork_jobs.learn` - Improve instructions based on execution learnings
-
-## Directory Structure
-
-```
-.
-├── AGENTS.md # This file - project context and guidance
-├── job.yml # Job specification (created by /deepwork_jobs.define)
-├── steps/ # Step instruction files (created by /deepwork_jobs.implement)
-│ └── *.md # One file per step
-├── hooks/ # Custom validation scripts and prompts
-│ └── *.md|*.sh # Hook files referenced in job.yml
-└── templates/ # Example file formats and templates
- └── *.md|*.yml # Templates referenced in step instructions
-```
-
-## Editing Guidelines
-
-1. **Use slash commands** for structural changes (adding steps, modifying job.yml)
-2. **Direct edits** are fine for minor instruction tweaks
-3. **Run `/deepwork_jobs.learn`** after executing job steps to capture improvements
-4. **Run `deepwork sync`** after any changes to regenerate commands
-EOF
-
- info "Created directory structure:"
- echo " $job_path/"
- echo " ├── AGENTS.md"
- echo " ├── steps/"
- echo " ├── hooks/.gitkeep"
- echo " └── templates/.gitkeep"
-
- echo ""
- info "Next steps:"
- echo " 1. Run '/deepwork_jobs.define' to create the job.yml specification"
- echo " 2. Run '/deepwork_jobs.implement' to generate step instructions"
- echo " 3. Run 'deepwork sync' to create slash commands"
-}
-
-main "$@"
diff --git a/.deepwork/jobs/deepwork_jobs/steps/define.md b/.deepwork/jobs/deepwork_jobs/steps/define.md
deleted file mode 100644
index 31de7440..00000000
--- a/.deepwork/jobs/deepwork_jobs/steps/define.md
+++ /dev/null
@@ -1,458 +0,0 @@
-# Define Job Specification
-
-## Objective
-
-Create a `job.yml` specification file that defines the structure of a new DeepWork job by thoroughly understanding the user's workflow requirements through an interactive question-and-answer process.
-
-## Task
-
-Guide the user through defining a job specification by asking structured questions. **Do not attempt to create the specification without first fully understanding the user's needs.**
-
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
-
-The output of this step is **only** the `job.yml` file - a complete specification of the workflow. The actual step instruction files will be created in the next step (`implement`).
-
-### Step 1: Understand the Job Purpose
-
-Start by asking structured questions to understand what the user wants to accomplish:
-
-1. **What is the overall goal of this workflow?**
- - What complex task are they trying to accomplish?
- - What domain is this in? (e.g., research, marketing, development, reporting)
- - How often will they run this workflow?
-
-2. **What does success look like?**
- - What's the final deliverable or outcome?
- - Who is the audience for the output?
- - What quality criteria matter most?
-
-3. **What are the major phases?**
- - Ask them to describe the workflow at a high level
- - What are the distinct stages from start to finish?
- - Are there any dependencies between phases?
-
-### Step 1.5: Detect Document-Oriented Workflows
-
-**Check for document-focused patterns** in the user's description:
-- Keywords: "report", "summary", "document", "create", "monthly", "quarterly", "for stakeholders", "for leadership"
-- Final deliverable is a specific document (e.g., "AWS spending report", "competitive analysis", "sprint summary")
-- Recurring documents with consistent structure
-
-**If a document-oriented workflow is detected:**
-
-1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first to ensure consistent quality."
-
-2. Ask structured questions to understand if they want to:
- - Create a doc spec for this document
- - Use an existing doc spec (if any exist in `.deepwork/doc_specs/`)
- - Skip doc spec and proceed with simple outputs
-
-### Step 1.6: Define the Doc Spec (if needed)
-
-When creating a doc spec, gather the following information:
-
-1. **Document Identity**
- - What is the document called? (e.g., "Monthly AWS Spending Report")
- - Brief description of its purpose
- - Where should these documents be stored? (path patterns like `finance/aws-reports/*.md`)
-
-2. **Audience and Context**
- - Who reads this document? (target audience)
- - How often is it produced? (frequency)
-
-3. **Quality Criteria** (3-5 criteria, each with name and description)
-
- **Important**: Doc spec quality criteria define requirements for the **output document itself**, not the process of creating it. Focus on what the finished document must contain or achieve.
-
- Examples for a spending report:
- - **Visualization**: Must include charts showing spend breakdown by service
- - **Variance Analysis**: Must compare current month against previous with percentages
- - **Action Items**: Must include recommended cost optimization actions
-
- **Note**: When a doc spec is created for a step's output, the step should generally NOT have separate `quality_criteria` in the job.yml. The doc spec's criteria cover output quality. Only add step-level quality_criteria if there are essential process requirements (e.g., "must use specific tool"), and minimize these when possible.
-
-4. **Document Structure**
- - What sections should it have?
- - Any required elements (tables, charts, summaries)?
-
-### Step 1.7: Create the doc spec File (if needed)
-
-Create the doc spec file at `.deepwork/doc_specs/[doc_spec_name].md`:
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/doc_spec.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/doc_specs/job_spec.md` for a fully worked example (the doc spec for job.yml files).
-
-After creating the doc spec, proceed to Step 2 with the doc spec reference for the final step's output.
-
-### Step 2: Define Each Step
-
-For each major phase they mentioned, ask structured questions to gather details:
-
-1. **Step Purpose**
- - What exactly does this step accomplish?
- - What is the input to this step?
- - What is the output from this step?
-
-2. **Step Inputs**
- - What information is needed to start this step?
- - Does it need user-provided parameters? (e.g., topic, target audience)
- - Does it need files from previous steps?
- - What format should inputs be in?
-
-3. **Step Outputs**
- - What files or artifacts does this step produce?
- - What format should the output be in? (markdown, YAML, JSON, etc.)
- - Where should each output be saved? (filename/path)
- - Should outputs be organized in subdirectories? (e.g., `reports/`, `data/`, `drafts/`)
- - Will other steps need this output?
- - **Does this output have a doc spec?** If a doc spec was created in Step 1.6/1.7, reference it for the appropriate output
-
- #### Work Product Storage Guidelines
-
- **Key principle**: Job outputs belong in the main repository directory structure, not in dot-directories. The `.deepwork/` directory is for job definitions and configuration only.
-
- **Why this matters**:
- - **Version control**: Work products in the main repo are tracked by git and visible in PRs
- - **Discoverability**: Team members can find outputs without knowing about DeepWork internals
- - **Tooling compatibility**: IDEs, search tools, and CI/CD work naturally with standard paths
- - **Glob patterns**: Well-structured paths enable powerful file matching (e.g., `competitive_research/**/*.md`)
-
- **Good output path patterns**:
- ```
- competitive_research/competitors_list.md
- competitive_research/acme_corp/research.md
- operations/reports/2026-01/spending_analysis.md
- docs/api/endpoints.md
- ```
-
- **Avoid these patterns**:
- ```
- .deepwork/outputs/report.md # Hidden in dot-directory
- output.md # Too generic, no context
- research.md # Unclear which research
- temp/draft.md # Transient-sounding paths
- ```
-
- **Organizing multi-file outputs**:
- - Use the job name as a top-level folder when outputs are job-specific
- - Use parameterized paths for per-entity outputs: `competitive_research/[competitor_name]/`
- - Match existing project conventions when extending a codebase
-
- **When to include dates in paths**:
- - **Include date** for periodic outputs where each version is retained (e.g., monthly reports, quarterly reviews, weekly summaries). These accumulate over time and historical versions remain useful.
- ```
- operations/reports/2026-01/spending_analysis.md # Monthly report - keep history
- hr/employees/[employee_name]/quarterly_reviews/2026-Q1.pdf # Per-employee quarterly review
- ```
- - **Omit date** for current-state outputs that represent the latest understanding and get updated in place. Previous versions live in git history, not separate files.
- ```
- competitive_research/acme_corp/swot.md # Current SWOT - updated over time
- docs/architecture/overview.md # Living document
- ```
-
- **Supporting materials and intermediate outputs**:
- - Content generated in earlier steps to support the final output (research notes, data extracts, drafts) should be placed in a `_dataroom` folder that is a peer to the final output
- - Name the dataroom folder by replacing the file extension with `_dataroom`
- ```
- operations/reports/2026-01/spending_analysis.md # Final output
- operations/reports/2026-01/spending_analysis_dataroom/ # Supporting materials
- raw_data.csv
- vendor_breakdown.md
- notes.md
- ```
- - This keeps supporting materials organized and discoverable without cluttering the main output location
-
-4. **Step Dependencies**
- - Which previous steps must complete before this one?
- - Are there any ordering constraints?
-
-5. **Step Process** (high-level understanding)
- - What are the key activities in this step?
- - Are there any quality checks or validation needed?
- - What makes a good vs. bad output for this step?
-
-6. **Agent Delegation** (optional)
- - Should this step be executed by a specific agent type?
- - Use the `agent` field when the step should run in a forked context with a specific agent
- - When `agent` is set, the generated skill automatically includes `context: fork`
- - Available agent types:
- - `general-purpose` - Standard agent for multi-step tasks
-
- ```yaml
- steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
- ```
-
-**Note**: You're gathering this information to understand what instructions will be needed, but you won't create the instruction files yet - that happens in the `implement` step.
-
-#### Doc Spec-Aware Output Format
-
-When a step produces a document with a doc spec reference, use this format in job.yml:
-
-```yaml
-outputs:
- - file: reports/monthly_spending.md
- doc_spec: .deepwork/doc_specs/monthly_aws_report.md
-```
-
-The doc spec's quality criteria will automatically be included in the generated skill, ensuring consistent document quality.
-
-### Capability Considerations
-
-When defining steps, identify any that require specialized tools:
-
-**Browser Automation**: If any step involves web scraping, form filling, interactive browsing, UI testing, or research requiring website visits, ask the user what browser tools they have available. For Claude Code users, **Claude in Chrome** (Anthropic's browser extension) has been tested with DeepWork and is recommended for new users. Don't assume a default—confirm the tool before designing browser-dependent steps.
-
-### Step 3: Validate the Workflow
-
-After gathering information about all steps:
-
-1. **Review the flow**
- - Summarize the complete workflow
- - Show how outputs from one step feed into the next
- - Ask if anything is missing
-
-2. **Check for gaps**
- - Are there any steps where the input isn't clearly defined?
- - Are there any outputs that aren't used by later steps?
- - Are there circular dependencies?
-
-3. **Confirm details**
- - Job name (lowercase, underscores, descriptive)
- - Job summary (one clear sentence, max 200 chars)
- - Job description (detailed multi-line explanation)
- - Version number (start with 1.0.0)
-
-### Step 4: Define Quality Validation (Stop Hooks)
-
-For each step, consider whether it would benefit from **quality validation loops**. Stop hooks allow the AI agent to iteratively refine its work until quality criteria are met.
-
-**Ask structured questions about quality validation:**
-- "Are there specific quality criteria that must be met for this step?"
-- "Would you like the agent to validate its work before completing?"
-- "What would make you send the work back for revision?"
-
-**Stop hooks are particularly valuable for:**
-- Steps with complex outputs that need multiple checks
-- Steps where quality is critical (final deliverables)
-- Steps with subjective quality criteria that benefit from AI self-review
-
-**Three types of stop hooks are supported:**
-
-1. **Inline Prompt** (`prompt`) - Best for simple quality criteria
- ```yaml
- stop_hooks:
- - prompt: |
- Verify the output meets these criteria:
- 1. Contains at least 5 competitors
- 2. Each competitor has a description
- 3. Selection rationale is clear
- ```
-
-2. **Prompt File** (`prompt_file`) - For detailed/reusable criteria
- ```yaml
- stop_hooks:
- - prompt_file: hooks/quality_check.md
- ```
-
-3. **Script** (`script`) - For programmatic validation (tests, linting)
- ```yaml
- stop_hooks:
- - script: hooks/run_tests.sh
- ```
-
-**Multiple hooks can be combined:**
-```yaml
-stop_hooks:
- - script: hooks/lint_output.sh
- - prompt: "Verify the content is comprehensive and well-organized"
-```
-
-**Encourage prompt-based hooks** - They leverage the AI's ability to understand context and make nuanced quality judgments. Script hooks are best for objective checks (syntax, format, tests).
-
-### Step 5: Create the Job Directory and Specification
-
-Only after you have complete understanding, create the job directory and `job.yml` file:
-
-**First, create the directory structure** using the `make_new_job.sh` script:
-
-```bash
-.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
-```
-
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - For step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - For custom validation scripts
-- `.deepwork/jobs/[job_name]/templates/` - For example file formats
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Then create the job.yml file** at `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the NEW job you're creating, e.g., `competitive_research`)
-
-**Doc Spec**: See `.deepwork/doc_specs/job_spec.md` for the complete specification with quality criteria.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.example` for a fully worked example.
-
-**Important**:
-- Use lowercase with underscores for job name and step IDs
-- Ensure file inputs reference steps in dependencies
-- No circular dependencies
-- At least one output per step
-- The `summary` should be concise (max 200 chars)
-- The `description` should provide rich context for future refinement
-- Include a `changelog` section with an initial entry for version 1.0.0
-
-## Example Dialog
-
-```
-Claude: I'll help you define a new DeepWork job. Let's start by understanding what you're trying to accomplish.
-
-What complex task or workflow are you trying to create? What's the overall goal?
-
-User: I want to create competitive research reports for my company
-
-Claude: Great! Let's dig into that. A few questions:
-
-1. What's the final deliverable? Is it a written report, a presentation, or something else?
-2. Who is the audience? Your team, executives, clients?
-3. How comprehensive should these reports be?
-
-User: It's a written report for our product team. Should be comprehensive - analyzing 3-5 competitors.
-
-Claude: Perfect. Now let's think about the major phases of creating this report. What are the main stages you go through? For example:
-- Identifying which competitors to analyze?
-- Gathering information about them?
-- Analyzing the data?
-- Writing the report?
-
-Or is your process different?
-
-User: Yes, that's close. I would say:
-1. Identify competitors
-2. Research each one
-3. Create comparison analysis
-4. Write positioning recommendations
-
-Claude: Excellent! Now let's define each step in detail. Starting with "Identify competitors":
-
-1. What inputs do you need to start? Do you already have a market segment in mind, or do you need to specify that?
-2. What should the output look like? A list of competitor names? More detailed?
-3. How many competitors should be identified?
-
-User: I need to specify the market segment and product category as inputs. The output should be a markdown file with 5-7 competitors and brief descriptions of each.
-
-Claude: Perfect. A few more questions about that output:
-1. What should the file be called? Something like `competitors_list.md`?
-2. Should it go in the project root, or in a subdirectory like `research/` or `data/`?
-
-User: Just call it `competitors_list.md` in the project root.
-
-Claude: Great. Now for the "Research each one" step:
-1. Does it use the competitor list from the first step as input?
-2. What kind of research - product features, pricing, marketing, all of the above?
-3. What format should the research output be in?
-4. Where should that research be saved?
-
-[continues for each step...]
-
-[After gathering all information:]
-
-Claude: Let me summarize the workflow I've designed based on our conversation:
-
-**Job: competitive_research**
-Summary: Systematic competitive analysis workflow for product positioning
-
-Description:
-A comprehensive workflow for analyzing competitors in your market segment. This job helps product teams understand the competitive landscape by systematically identifying competitors, researching their offerings, creating comparison matrices, and developing strategic positioning recommendations.
-
-The workflow produces:
-- A vetted list of key competitors
-- Detailed research notes on each competitor
-- A comparison matrix highlighting key differentiators
-- Strategic positioning recommendations
-
-Designed for product teams conducting quarterly competitive analysis.
-
-**Steps:**
-
-Step 1: identify_competitors
-- Input: market_segment (user), product_category (user)
-- Output: competitors_list.md (5-7 competitors with descriptions)
-
-Step 2: research_competitors
-- Input: competitors_list.md (from step 1)
-- Output: research_notes.md (detailed findings for each competitor)
-- Depends on: identify_competitors
-
-Step 3: comparative_analysis
-- Input: research_notes.md (from step 2)
-- Output: comparison_matrix.md (side-by-side analysis)
-- Depends on: research_competitors
-
-Step 4: positioning_recommendations
-- Input: comparison_matrix.md (from step 3)
-- Output: positioning_report.md (strategic recommendations)
-- Depends on: comparative_analysis
-
-Does this accurately capture your workflow? Any changes needed?
-
-User: Yes, that's perfect!
-
-Claude: Great! Creating the job.yml specification now...
-
-[Creates .deepwork/jobs/competitive_research/job.yml with the complete spec]
-
-✓ Job specification created!
-
-**File created:**
-- .deepwork/jobs/competitive_research/job.yml
-
-**Next step:**
-Run `/deepwork_jobs.review_job_spec` to validate the specification against quality criteria.
-```
-
-## Important Guidelines
-
-1. **Focus on specification only** - Don't create instruction files yet
-2. **Ask structured questions** - Never skip the discovery phase; use the AskUserQuestion tool
-3. **Rich context in description** - This helps with future refinement
-4. **Validate understanding** - Summarize and confirm before creating
-5. **Use examples** - Help users understand what good specifications look like
-6. **Understand file organization** - Always ask structured questions about where outputs should be saved and if subdirectories are needed
-
-## Validation Rules
-
-Before creating the job.yml, ensure:
-- Job name: lowercase, underscores, no spaces
-- Version: semantic versioning (1.0.0)
-- Summary: concise, under 200 characters
-- Description: detailed, provides context
-- Step IDs: unique, descriptive, lowercase with underscores
-- Dependencies: must reference existing step IDs
-- File inputs: `from_step` must be in dependencies
-- At least one output per step
-- Outputs can be filenames (e.g., `report.md`) or paths (e.g., `reports/analysis.md`)
-- File paths in outputs should match where files will actually be created
-- No circular dependencies
-
-## Output Format
-
-### job.yml
-
-The complete YAML specification file (example shown in Step 5 above).
-
-**Location**: `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the new job being created)
-
-After creating the file:
-1. Inform the user that the specification is complete
-2. Recommend that they review the job.yml file
-3. Tell them to run `/deepwork_jobs.review_job_spec` next
-
diff --git a/.deepwork/jobs/deepwork_jobs/steps/implement.md b/.deepwork/jobs/deepwork_jobs/steps/implement.md
deleted file mode 100644
index 749c8c6f..00000000
--- a/.deepwork/jobs/deepwork_jobs/steps/implement.md
+++ /dev/null
@@ -1,233 +0,0 @@
-# Implement Job Steps
-
-## Objective
-
-Generate the DeepWork job directory structure and instruction files for each step based on the validated `job.yml` specification from the review_job_spec step.
-
-## Task
-
-Read the `job.yml` specification file and create all the necessary files to make the job functional, including directory structure and step instruction files. Then sync the commands to make them available.
-
-### Step 1: Create Directory Structure Using Script
-
-Run the `make_new_job.sh` script to create the standard directory structure:
-
-```bash
-.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
-```
-
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - Step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - Custom validation scripts (with .gitkeep)
-- `.deepwork/jobs/[job_name]/templates/` - Example file formats (with .gitkeep)
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Note**: If the directory already exists (e.g., job.yml was created by define step), you can skip this step or manually create the additional directories:
-```bash
-mkdir -p .deepwork/jobs/[job_name]/hooks .deepwork/jobs/[job_name]/templates
-touch .deepwork/jobs/[job_name]/hooks/.gitkeep .deepwork/jobs/[job_name]/templates/.gitkeep
-```
-
-### Step 2: Read and Validate the Specification
-
-1. **Locate the job.yml file**
- - Read `.deepwork/jobs/[job_name]/job.yml` from the review_job_spec step
- - Parse the YAML content
-
-2. **Validate the specification**
- - Ensure it follows the schema (name, version, summary, description, steps)
- - Check that all dependencies reference existing steps
- - Verify no circular dependencies
- - Confirm file inputs match dependencies
-
-3. **Extract key information**
- - Job name, version, summary, description
- - List of all steps with their details
- - Understand the workflow structure
-
-### Step 3: Generate Step Instruction Files
-
-For each step in the job.yml, create a comprehensive instruction file at `.deepwork/jobs/[job_name]/steps/[step_id].md`.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example` for a fully worked example.
-
-**Available templates in `.deepwork/jobs/deepwork_jobs/templates/`:**
-- `job.yml.template` - Job specification structure
-- `step_instruction.md.template` - Step instruction file structure
-- `agents.md.template` - AGENTS.md file structure
-- `job.yml.example` - Complete job specification example
-- `step_instruction.md.example` - Complete step instruction example
-
-**Guidelines for generating instructions:**
-
-1. **Use the job description** - The detailed description from job.yml provides crucial context
-2. **Be specific** - Don't write generic instructions; tailor them to the step's purpose
-3. **Provide examples** - Show what good output looks like
-4. **Explain the "why"** - Help the user understand the step's role in the workflow
-5. **Quality over quantity** - Detailed, actionable instructions are better than vague ones
-6. **Align with stop hooks** - If the step has `stop_hooks` defined, ensure the quality criteria in the instruction file match the validation criteria in the hooks
-7. **Ask structured questions** - When a step has user inputs, the instructions MUST explicitly tell the agent to "ask structured questions" using the AskUserQuestion tool to gather that information. Never use generic phrasing like "ask the user" - always use "ask structured questions"
-
-### Handling Stop Hooks
-
-If a step in the job.yml has `stop_hooks` defined, the generated instruction file should:
-
-1. **Mirror the quality criteria** - The "Quality Criteria" section should match what the stop hooks will validate
-2. **Be explicit about success** - Help the agent understand when the step is truly complete
-3. **Include the promise pattern** - Mention that `✓ Quality Criteria Met` should be included when criteria are met
-
-**Example: If the job.yml has:**
-```yaml
-- id: research_competitors
- name: "Research Competitors"
- stop_hooks:
- - prompt: |
- Verify the research meets criteria:
- 1. Each competitor has at least 3 data points
- 2. Sources are cited
- 3. Information is current (within last year)
-```
-
-**The instruction file should include:**
-```markdown
-## Quality Criteria
-
-- Each competitor has at least 3 distinct data points
-- All information is sourced with citations
-- Data is current (from within the last year)
-- When all criteria are met, include `✓ Quality Criteria Met` in your response
-```
-
-This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing.
-
-### Using Supplementary Reference Files
-
-Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root.
-
-See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples.
-
-### Step 4: Verify job.yml Location
-
-Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define and review_job_spec steps should have created and validated it. If for some reason it's not there, you may need to create or move it.
-
-### Step 5: Sync Skills
-
-Run `deepwork sync` to generate the skills for this job:
-
-```bash
-deepwork sync
-```
-
-This will:
-- Parse the job definition
-- Generate skills for each step
-- Make the skills available in `.claude/skills/` (or appropriate platform directory)
-
-### Step 6: Consider Rules for the New Job
-
-After implementing the job, consider whether there are **rules** that would help enforce quality or consistency when working with this job's domain.
-
-**What are rules?**
-
-Rules are automated guardrails stored as markdown files in `.deepwork/rules/` that trigger when certain files change during an AI session. They help ensure:
-- Documentation stays in sync with code
-- Team guidelines are followed
-- Architectural decisions are respected
-- Quality standards are maintained
-
-**When to suggest rules:**
-
-Think about the job you just implemented and ask:
-- Does this job produce outputs that other files depend on?
-- Are there documentation files that should be updated when this job's outputs change?
-- Are there quality checks or reviews that should happen when certain files in this domain change?
-- Could changes to the job's output files impact other parts of the project?
-
-**Examples of rules that might make sense:**
-
-| Job Type | Potential Rule |
-|----------|----------------|
-| API Design | "Update API docs when endpoint definitions change" |
-| Database Schema | "Review migrations when schema files change" |
-| Competitive Research | "Update strategy docs when competitor analysis changes" |
-| Feature Development | "Update changelog when feature files change" |
-| Configuration Management | "Update install guide when config files change" |
-
-**How to offer rule creation:**
-
-If you identify one or more rules that would benefit the user, explain:
-1. **What the rule would do** - What triggers it and what action it prompts
-2. **Why it would help** - How it prevents common mistakes or keeps things in sync
-3. **What files it would watch** - The trigger patterns
-
-Then ask the user:
-
-> "Would you like me to create this rule for you? I can run `/deepwork_rules.define` to set it up."
-
-If the user agrees, invoke the `/deepwork_rules.define` command to guide them through creating the rule.
-
-**Example dialogue:**
-
-```
-Based on the competitive_research job you just created, I noticed that when
-competitor analysis files change, it would be helpful to remind you to update
-your strategy documentation.
-
-I'd suggest a rule like:
-- **Name**: "Update strategy when competitor analysis changes"
-- **Trigger**: `**/positioning_report.md`
-- **Action**: Prompt to review and update `docs/strategy.md`
-
-Would you like me to create this rule? I can run `/deepwork_rules.define` to set it up.
-```
-
-**Note:** Not every job needs rules. Only suggest them when they would genuinely help maintain consistency or quality. Don't force rules where they don't make sense.
-
-## Example Implementation
-
-For a complete worked example showing a job.yml and corresponding step instruction file, see:
-- **Job specification**: `.deepwork/jobs/deepwork_jobs/templates/job.yml.example`
-- **Step instruction**: `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example`
-
-## Important Guidelines
-
-1. **Read the spec carefully** - Understand the job's intent from the description
-2. **Generate complete instructions** - Don't create placeholder or stub files
-3. **Maintain consistency** - Use the same structure for all step instruction files
-4. **Provide examples** - Show what good output looks like
-5. **Use context** - The job description provides valuable context for each step
-6. **Be specific** - Tailor instructions to the specific step, not generic advice
-
-## Validation Before Sync
-
-Before running `deepwork sync`, verify:
-- All directories exist
-- `job.yml` is in place
-- All step instruction files exist (one per step)
-- No file system errors
-
-## Completion Checklist
-
-Before marking this step complete, ensure:
-- [ ] job.yml validated and copied to job directory
-- [ ] All step instruction files created
-- [ ] Each instruction file is complete and actionable
-- [ ] `deepwork sync` executed successfully
-- [ ] Skills generated in platform directory
-- [ ] Considered whether rules would benefit this job (Step 6)
-- [ ] If rules suggested, offered to run `/deepwork_rules.define`
-
-## Quality Criteria
-
-- Job directory structure is correct
-- All instruction files are complete (not stubs)
-- Instructions are specific and actionable
-- Output examples are provided in each instruction file
-- Quality criteria defined for each step
-- Steps with user inputs explicitly use "ask structured questions" phrasing
-- Sync completed successfully
-- Skills available for use
-- Thoughtfully considered relevant rules for the job domain
diff --git a/.deepwork/jobs/deepwork_jobs/steps/learn.md b/.deepwork/jobs/deepwork_jobs/steps/learn.md
deleted file mode 100644
index bfb393a5..00000000
--- a/.deepwork/jobs/deepwork_jobs/steps/learn.md
+++ /dev/null
@@ -1,355 +0,0 @@
-# Learn from Job Execution
-
-## Objective
-
-Think deeply about this task. Reflect on the current conversation to identify learnings from DeepWork job executions, improve job instructions with generalizable insights, and capture bespoke (run-specific) learnings in AGENTS.md files in the deepest common folder that would contain all work on the topic in the future.
-
-## Task
-
-Analyze the conversation history to extract learnings and improvements, then apply them appropriately:
-- **Generalizable learnings** → Update job instruction files
-- **Bespoke learnings** (specific to this run) → Add to AGENTS.md in the deepest common folder for the topic
-
-### Step 1: Analyze Conversation for Job Executions
-
-1. **Scan the conversation** for DeepWork slash commands that were run
- - Look for patterns like `/job_name.step_id`
- - Identify which jobs and steps were executed
- - Note the order of execution
-
-2. **Identify the target folder**
- - This should be the deepest common folder that would contain all work on the topic in the future
- - Should be clear from conversation history where work was done
- - If unclear, run `git diff` to see where changes were made on the branch
-
-3. **If no job was specified**, ask the user:
- - "Which DeepWork job would you like me to learn from?"
- - List available jobs from `.deepwork/jobs/`
-
-### Step 2: Identify Points of Confusion and Inefficiency
-
-Review the conversation for:
-
-1. **Confusion signals**
- - Questions the agent asked that shouldn't have been necessary
- - Misunderstandings about what a step required
- - Incorrect outputs that needed correction
- - Ambiguous instructions that led to wrong interpretations
-
-2. **Inefficiency signals**
- - Extra steps or iterations that were needed
- - Information that had to be repeated
- - Context that was missing from instructions
- - Dependencies that weren't clear
-
-3. **Error patterns**
- - Failed validations and why they failed
- - Quality criteria that were misunderstood
- - Edge cases that weren't handled
-
-4. **Success patterns**
- - What worked particularly well
- - Efficient approaches worth preserving
- - Good examples that could be added to instructions
-
-### Step 3: Classify Learnings
-
-For each learning identified, determine if it is:
-
-**Generalizable** (should improve instructions):
-- Would help ANY future run of this job
-- Addresses unclear or missing guidance
-- Fixes incorrect assumptions in instructions
-- Adds helpful examples or context
-- Examples:
- - "Step instructions should mention that X format is required"
- - "Quality criteria should include checking for Y"
- - "Add example of correct output format"
-
-**doc spec-Related** (should improve doc spec files):
-- Improvements to document quality criteria
-- Changes to document structure or format
-- Updated audience or frequency information
-- Examples:
- - "The report should include a summary table"
- - "Quality criterion 'Visualization' needs clearer requirements"
- - "Documents need a section for action items"
-
-**Bespoke** (should go in AGENTS.md):
-- Specific to THIS project/codebase/run
-- Depends on local conventions or structure
-- References specific files or paths
-- Would not apply to other uses of this job
-- Examples:
- - "In this codebase, API endpoints are in `src/api/`"
- - "This project uses camelCase for function names"
- - "The main config file is at `config/settings.yml`"
-
-### Step 3.5: Identify doc spec-Related Learnings
-
-Review the conversation for doc spec-related improvements:
-
-1. **Quality Criteria Changes**
- - Were any quality criteria unclear or insufficient?
- - Did the agent repeatedly fail certain criteria?
- - Are there new criteria that should be added?
-
-2. **Document Structure Changes**
- - Did the user request different sections?
- - Were parts of the document format confusing?
- - Should the example document be updated?
-
-3. **Metadata Updates**
- - Has the target audience changed?
- - Should frequency or path patterns be updated?
-
-**Signals for doc spec improvements:**
-- User asked for changes to document format
-- Repeated validation failures on specific criteria
-- Feedback about missing sections or information
-- Changes to how documents are organized/stored
-
-### Step 4: Update Job Instructions (Generalizable Learnings)
-
-For each generalizable learning:
-
-1. **Locate the instruction file**
- - Path: `.deepwork/jobs/[job_name]/steps/[step_id].md`
-
-2. **Make targeted improvements**
- - Add missing context or clarification
- - Include helpful examples
- - Clarify ambiguous instructions
- - Update quality criteria if needed
-
-3. **Keep instructions concise**
- - Avoid redundancy - don't repeat the same guidance in multiple places
- - Be direct - remove verbose explanations that don't add value
- - Prefer bullet points over paragraphs where appropriate
-
-4. **Preserve instruction structure**
- - Keep existing sections (Objective, Task, Process, Output Format, Quality Criteria)
- - Add to appropriate sections rather than restructuring
- - Maintain consistency with other steps
-
-5. **Track changes for changelog**
- - Note what was changed and why
- - Prepare changelog entry for job.yml
-
-### Step 4b: Extract Shared Content into Referenced Files
-
-Review all instruction files for the job and identify content that:
-- Appears in multiple step instructions (duplicated)
-- Is lengthy and could be extracted for clarity
-- Would benefit from being maintained in one place
-
-**Extract to shared files:**
-
-1. **Create shared files** in `.deepwork/jobs/[job_name]/steps/shared/`
- - `conventions.md` - Coding/formatting conventions used across steps
- - `examples.md` - Common examples referenced by multiple steps
- - `schemas.md` - Data structures or formats used throughout
-
-2. **Reference from instructions** using markdown includes or explicit references:
- ```markdown
- ## Conventions
-
- Follow the conventions defined in `shared/conventions.md`.
- ```
-
-3. **Benefits of extraction:**
- - Single source of truth - update once, applies everywhere
- - Shorter instruction files - easier to read and maintain
- - Consistent guidance across steps
-
-### Step 4.5: Update doc spec Files (doc spec-Related Learnings)
-
-If doc spec-related learnings were identified:
-
-1. **Locate the doc spec file**
- - Find doc spec references in job.yml outputs (look for `doc_spec: .deepwork/doc_specs/[doc_spec_name].md`)
- - doc spec files are at `.deepwork/doc_specs/[doc_spec_name].md`
-
-2. **Update quality_criteria array**
- - Add new criteria with name and description
- - Modify existing criteria descriptions for clarity
- - Remove criteria that are no longer relevant
-
-3. **Update example document**
- - Modify the markdown body to reflect structure changes
- - Ensure the example matches updated criteria
-
-4. **Update metadata as needed**
- - target_audience: If audience has changed
- - frequency: If production cadence has changed
- - path_patterns: If storage location has changed
-
-**Example doc spec update:**
-```yaml
-# Before
-quality_criteria:
- - name: Visualization
- description: Include charts
-
-# After
-quality_criteria:
- - name: Visualization
- description: Include Mermaid.js charts showing spend breakdown by service and month-over-month trend
-```
-
-### Step 5: Create/Update AGENTS.md (Bespoke Learnings)
-
-The AGENTS.md file captures project-specific knowledge that helps future agent runs.
-
-1. **Determine the correct location**
- - Place AGENTS.md in the deepest common folder that would contain all work on the topic in the future
- - This ensures the knowledge is available when working in that context
- - If uncertain, place at the project root
-
-2. **Use file references where possible**
- - Instead of duplicating information, reference source files
- - This keeps AGENTS.md in sync as the codebase evolves
- - Pattern: "See `path/to/file.ext` for [description]"
-
-3. **AGENTS.md structure**: See `.deepwork/jobs/deepwork_jobs/templates/agents.md.template` for the standard format.
-
-4. **Writing entries**
- - Be concise but specific
- - Always prefer file references over inline content
- - Use line numbers when referencing specific code: `file.ext:42`
- - Group related learnings together
-
-### Step 6: Update Job Version and Changelog
-
-If instruction files were modified:
-
-1. **Bump version in job.yml**
- - Patch version (0.0.x) for instruction improvements
- - Minor version (0.x.0) if quality criteria changed
-
-2. **Add changelog entry**
- ```yaml
- - version: "[new_version]"
- changes: "Improved [step] instructions based on execution learnings: [brief description]"
- ```
-
-### Step 7: Sync Skills
-
-**Run deepwork sync** (if instructions were modified)
-```bash
-deepwork sync
-```
-
-## File Reference Patterns
-
-When adding entries to AGENTS.md, prefer these patterns:
-
-**Good (references)**:
-```markdown
-- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
-- Error handling pattern: See `src/utils/errors.ts:15-30`
-- Configuration schema: Defined in `config/schema.json`
-```
-
-**Avoid (duplicating)**:
-```markdown
-- API endpoints should return JSON with this format: { status: ..., data: ... }
-- Error handling should use try/catch with custom error classes
-- Configuration requires these fields: name, version, ...
-```
-
-## Quality Criteria
-
-- Conversation has been analyzed for job executions
-- Points of confusion and inefficiency are identified
-- Learnings are correctly classified (generalizable vs bespoke)
-- Job instructions updated for generalizable improvements
-- Instructions are concise - no redundancy or unnecessary verbosity
-- Shared/lengthy content extracted into referenced files where appropriate
-- AGENTS.md created/updated with bespoke learnings
-- File references used instead of duplicating content
-- AGENTS.md is in the correct folder (the deepest common folder for the topic)
-- When all criteria are met, include `✓ Quality Criteria Met`
-
-## Example Dialog
-
-```
-User: /deepwork_jobs.learn
-
-Claude: I'll analyze this conversation for learnings from DeepWork job executions.
-
-**Conversation Analysis**
-
-I found the following job executions:
-1. `/competitive_research.identify_competitors` - Completed
-2. `/competitive_research.research_competitors` - Completed with 2 iterations
-3. `/competitive_research.comparative_analysis` - Completed
-
-**Identified Issues**
-
-1. **Confusion in step 2**: The instructions didn't specify that competitor research should include pricing information. This caused an extra iteration.
-
-2. **Missing context**: The agent had to ask about the industry focus, which should have been carried forward from step 1.
-
-**Classification**
-
-- Generalizable: Add pricing to research requirements in instructions
-- Bespoke: This project focuses on B2B SaaS in the HR tech space
-
-**Actions Taken**
-
-1. Updated `.deepwork/jobs/competitive_research/steps/research_competitors.md`:
- - Added "Include pricing information (tiers, models, approximate ranges)" to research requirements
- - Added note about carrying forward industry context from previous step
-
-2. Created `./competitive_research_output/AGENTS.md`:
- ```markdown
- # Project Context for Competitive Research
-
- ## Job-Specific Context
-
- ### competitive_research
-
- #### Industry Focus
- - Domain: B2B SaaS, HR Technology sector
- - See `identify_competitors_output.md` for market definition
-
- #### Competitors Identified
- - Reference: `competitor_list.md` for full details
-
- ## Last Updated
- - Date: 2024-01-15
- - From conversation about: Initial competitive analysis run
- ```
-
-3. Updated job.yml version to 1.0.1 with changelog entry
-
-4. Ran `deepwork sync`
-
-**Summary**
-
-Updated job instructions and created AGENTS.md with bespoke learnings.
-```
-
-## Handling Edge Cases
-
-**No job executions found**:
-- Ask user which job they'd like to analyze
-- Or offer to review available jobs
-
-**Multiple jobs executed**:
-- Analyze each job separately
-- Create separate AGENTS.md entries or files as appropriate
-
-**AGENTS.md already exists**:
-- Read existing content
-- Append new learnings to appropriate sections
-- Update "Last Updated" section
-
-**No issues found**:
-- Document what worked well
-- Consider if any successful patterns should be added to instructions as examples
-
-**Sensitive information**:
-- Never include secrets, credentials, or PII in AGENTS.md
-- Reference config files instead of including values
diff --git a/.deepwork/jobs/deepwork_jobs/steps/review_job_spec.md b/.deepwork/jobs/deepwork_jobs/steps/review_job_spec.md
deleted file mode 100644
index fcc0ae9c..00000000
--- a/.deepwork/jobs/deepwork_jobs/steps/review_job_spec.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# Review Job Specification
-
-## Objective
-
-Review the `job.yml` created in the define step against the doc spec quality criteria using a sub-agent for unbiased evaluation, then iterate on fixes until all criteria pass.
-
-## Why This Step Exists
-
-The define step focuses on understanding user requirements and creating a job specification. This review step ensures the specification meets quality standards before implementation. Using a sub-agent provides an unbiased "fresh eyes" review that catches issues the main agent might miss after being deeply involved in the definition process.
-
-## Task
-
-Use a sub-agent to review the job.yml against all 9 doc spec quality criteria, then fix any failed criteria. Repeat until all criteria pass.
-
-### Step 1: Read the Job Specification
-
-Read the `job.yml` file created in the define step:
-
-```
-.deepwork/jobs/[job_name]/job.yml
-```
-
-Also read the doc spec for reference:
-
-```
-.deepwork/doc_specs/job_spec.md
-```
-
-### Step 2: Spawn Review Sub-Agent
-
-Use the Task tool to spawn a sub-agent that will provide an unbiased review:
-
-```
-Task tool parameters:
-- subagent_type: "general-purpose"
-- model: "haiku"
-- description: "Review job.yml against doc spec"
-- prompt: [see below]
-```
-
-**Sub-agent prompt template:**
-
-```
-Review this job.yml against the following 9 quality criteria from the doc spec.
-
-For each criterion, respond with:
-- PASS or FAIL
-- If FAIL: specific issue and suggested fix
-
-## job.yml Content
-
-[paste the full job.yml content here]
-
-## Quality Criteria
-
-1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
-
-2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
-
-3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
-
-4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
-
-5. **Changelog Present**: Must include a changelog array with at least the initial version entry
-
-6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
-
-7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
-
-8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
-
-9. **Output Paths**: Outputs must be valid filenames or paths (e.g., `report.md` or `reports/analysis.md`)
-
-## Response Format
-
-Respond with a structured evaluation:
-
-### Overall: [X/9 PASS]
-
-### Criterion Results
-
-1. Valid Identifier: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-2. Semantic Version: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-[... continue for all 9 criteria ...]
-
-### Summary of Required Fixes
-
-[List any fixes needed, or "No fixes required - all criteria pass"]
-```
-
-### Step 3: Review Sub-Agent Findings
-
-Parse the sub-agent's response:
-
-1. **Count passing criteria** - How many of the 9 criteria passed?
-2. **Identify failures** - List specific criteria that failed
-3. **Note suggested fixes** - What changes does the sub-agent recommend?
-
-### Step 4: Fix Failed Criteria
-
-For each failed criterion, edit the job.yml to address the issue:
-
-**Common fixes by criterion:**
-
-| Criterion | Common Issue | Fix |
-|-----------|-------------|-----|
-| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
-| Semantic Version | Missing or invalid format | Set to `"1.0.0"` or fix format |
-| Concise Summary | Too long or vague | Shorten to <200 chars, be specific |
-| Rich Description | Single line or missing context | Add multi-line explanation with problem/process/outcome/users |
-| Changelog Present | Missing changelog | Add `changelog:` with initial version entry |
-| Complete Steps | Missing required fields | Add id, name, description, instructions_file, outputs, dependencies |
-| Valid Dependencies | Non-existent step or circular | Fix step ID reference or reorder dependencies |
-| Input Consistency | from_step not in dependencies | Add the referenced step to dependencies array |
-| Output Paths | Invalid characters or format | Use valid filename/path format |
-
-### Step 5: Re-Run Review (If Needed)
-
-If any criteria failed:
-
-1. **Spawn a new sub-agent** with the updated job.yml content
-2. **Review the new findings**
-3. **Fix any remaining issues**
-4. **Repeat until all 9 criteria pass**
-
-### Step 6: Confirm Completion
-
-When all 9 criteria pass:
-
-1. **Announce success**: "All 9 doc spec quality criteria pass."
-2. **List what was validated**:
- - Valid Identifier
- - Semantic Version
- - Concise Summary
- - Rich Description
- - Changelog Present
- - Complete Steps
- - Valid Dependencies
- - Input Consistency
- - Output Paths
-3. **Include the promise**: `Quality Criteria Met`
-4. **Guide to next step**: "Run `/deepwork_jobs.implement` to generate the step instruction files."
-
-## Example Review Cycle
-
-```
-Claude: Let me review the job.yml against the doc spec quality criteria.
-
-[Spawns sub-agent with haiku model]
-
-Sub-agent response:
-### Overall: 7/9 PASS
-
-### Criterion Results
-1. Valid Identifier: PASS
-2. Semantic Version: PASS
-3. Concise Summary: FAIL
- Issue: Summary is 245 characters, exceeds 200 char limit
- Fix: Shorten to "Competitive analysis workflow for product positioning"
-4. Rich Description: PASS
-5. Changelog Present: FAIL
- Issue: No changelog array present
- Fix: Add changelog with initial version entry
-6. Complete Steps: PASS
-7. Valid Dependencies: PASS
-8. Input Consistency: PASS
-9. Output Paths: PASS
-
-### Summary of Required Fixes
-1. Shorten summary to under 200 characters
-2. Add changelog array with initial version
-
-Claude: I found 2 issues. Let me fix them.
-
-[Edits job.yml to shorten summary and add changelog]
-
-Claude: Let me verify the fixes with another review.
-
-[Spawns new sub-agent]
-
-Sub-agent response:
-### Overall: 9/9 PASS
-
-### Summary of Required Fixes
-No fixes required - all criteria pass
-
-Claude: All 9 doc spec quality criteria pass.
-
-Quality Criteria Met
-
-**Next step:** Run `/deepwork_jobs.implement` to generate the step instruction files.
-```
-
-## Quality Criteria
-
-- **Sub-Agent Used**: A sub-agent was spawned to provide unbiased review (not just self-review)
-- **All doc spec Criteria Evaluated**: The sub-agent assessed all 9 quality criteria from the doc spec
-- **Findings Addressed**: All failed criteria were fixed by the main agent
-- **Validation Loop Complete**: The review-fix cycle continued until all criteria passed
-- **Promise Included**: The response includes `Quality Criteria Met` when complete
-
-## Output
-
-The validated `job.yml` file at `.deepwork/jobs/[job_name]/job.yml` that passes all 9 doc spec quality criteria.
diff --git a/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md b/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md
deleted file mode 100644
index 81b6494a..00000000
--- a/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Supplementary Reference Files
-
-Step instructions can include additional `.md` files in the `steps/` directory. These supplementary files are useful for:
-
-- Providing detailed examples or templates that would clutter the main instruction file
-- Sharing common reference material across multiple steps
-- Including technical specifications, API documentation, or style guides
-
-## How to Use
-
-1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files
-2. Reference them in your step instructions using the **full path from the project root**
-
-## Example
-
-If you have a job called `my_job` and want to include an API specification template:
-
-1. Create the file at `.deepwork/jobs/my_job/steps/api_spec.md`
-2. Reference it in your step instructions like this:
-
-```markdown
-Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints.
-```
-
-## Path Format
-
-Always use the full relative path from the project root:
-
-```
-.deepwork/jobs/[job_name]/steps/[filename].md
-```
-
-For example:
-- `.deepwork/jobs/competitive_research/steps/competitor_template.md`
-- `.deepwork/jobs/api_design/steps/endpoint_schema.md`
-- `.deepwork/jobs/onboarding/steps/checklist_template.md`
-
-## Benefits
-
-Using supplementary files keeps your main step instructions focused and readable while allowing you to provide detailed reference material when needed. The AI agent can read these files during execution to get additional context.
diff --git a/.deepwork/jobs/deepwork_rules/job.yml b/.deepwork/jobs/deepwork_rules/job.yml
deleted file mode 100644
index a0032b9e..00000000
--- a/.deepwork/jobs/deepwork_rules/job.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: deepwork_rules
-version: "0.4.0"
-summary: "Creates file-change rules that enforce guidelines during AI sessions. Use when automating documentation sync or code review triggers."
-description: |
- Manages rules that automatically trigger when certain files change during an AI agent session.
- Rules help ensure that code changes follow team guidelines, documentation is updated,
- and architectural decisions are respected.
-
- IMPORTANT: Rules are evaluated at the "Stop" hook, which fires when an agent finishes its turn.
- This includes when sub-agents complete their work. Rules are NOT evaluated immediately after
- each file edit - they batch up and run once at the end of the agent's response cycle.
- - Command action rules: Execute their command (e.g., `uv sync`) when the agent stops
- - Prompt action rules: Display instructions to the agent, blocking until addressed
-
- Rules are stored as individual markdown files with YAML frontmatter in the `.deepwork/rules/`
- directory. Each rule file specifies:
- - Detection mode: trigger/safety, set (bidirectional), or pair (directional)
- - Patterns: Glob patterns for matching files, with optional variable capture
- - Action type: prompt (default) to show instructions, or command to run a shell command
- - Instructions: Markdown content describing what the agent should do
-
- Example use cases:
- - Update installation docs when configuration files change
- - Require security review when authentication code is modified
- - Ensure API documentation stays in sync with API code
- - Enforce source/test file pairing
- - Auto-run `uv sync` when pyproject.toml changes (command action)
-
-changelog:
- - version: "0.1.0"
- changes: "Initial version"
- - version: "0.2.0"
- changes: "Standardized on 'ask structured questions' phrasing for user input"
- - version: "0.3.0"
- changes: "Migrated to v2 format - individual markdown files in .deepwork/rules/"
- - version: "0.4.0"
- changes: "Improved skill descriptions with third-person voice and 'Use when...' triggers for better discoverability"
-
-steps:
- - id: define
- name: "Define Rule"
- description: "Creates a rule file that triggers when specified files change. Use when setting up documentation sync, code review requirements, or automated commands."
- instructions_file: steps/define.md
- inputs:
- - name: rule_purpose
- description: "What guideline or constraint should this rule enforce?"
- outputs:
- - .deepwork/rules/{rule-name}.md
- dependencies: []
diff --git a/.deepwork/jobs/deepwork_rules/rules/.gitkeep b/.deepwork/jobs/deepwork_rules/rules/.gitkeep
deleted file mode 100644
index 429162b4..00000000
--- a/.deepwork/jobs/deepwork_rules/rules/.gitkeep
+++ /dev/null
@@ -1,13 +0,0 @@
-# This directory contains example rule templates.
-# Copy and customize these files to create your own rules.
-#
-# Rule files use YAML frontmatter in markdown format:
-#
-# ---
-# name: Rule Name
-# trigger: "pattern/**/*"
-# safety: "optional/pattern"
-# ---
-# Instructions in markdown here.
-#
-# See doc/rules_syntax.md for full documentation.
diff --git a/.deepwork/rules/skill-template-best-practices.md b/.deepwork/rules/skill-template-best-practices.md
index ff33ecfd..bdcd6d3e 100644
--- a/.deepwork/rules/skill-template-best-practices.md
+++ b/.deepwork/rules/skill-template-best-practices.md
@@ -1,6 +1,6 @@
---
name: Skill Template Best Practices
-trigger: src/deepwork/templates/**/skill-job*.jinja
+trigger: src/deepwork/templates/**/skill-workflow*.jinja
compare_to: prompt
---
Skill template files are being modified. Ensure the generated skills follow these best practices:
diff --git a/.gemini/skills/deepwork-rules.define/SKILL.md b/.gemini/skills/deepwork-rules.define/SKILL.md
new file mode 100644
index 00000000..97cbae3a
--- /dev/null
+++ b/.gemini/skills/deepwork-rules.define/SKILL.md
@@ -0,0 +1,328 @@
+# :define
+#
+# Create a new rule file that triggers when specified files change
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Create a new rule file that triggers when specified files change"
+
+prompt = """
+# :define
+
+**Step /** in **** workflow
+
+>
+
+
+## Instructions
+
+**Goal**: Create a new rule file that triggers when specified files change
+
+# Define Rule
+
+## Objective
+
+Create a new rule file in the `.deepwork/rules/` directory to enforce team guidelines, documentation requirements, or other constraints when specific files change.
+
+## Task
+
+Guide the user through defining a new rule by asking structured questions. **Do not create the rule without first understanding what they want to enforce.**
+
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
+
+### Step 1: Understand the Rule Purpose
+
+Start by asking structured questions to understand what the user wants to enforce:
+
+1. **What guideline or constraint should this rule enforce?**
+ - What situation triggers the need for action?
+ - What files or directories, when changed, should trigger this rule?
+ - Examples: "When config files change", "When API code changes", "When database schema changes"
+
+2. **What action should be taken?**
+ - What should the agent do when the rule triggers?
+ - Update documentation? Perform a security review? Update tests?
+ - Is there a specific file or process that needs attention?
+
+3. **Are there any "safety" conditions?**
+ - Are there files that, if also changed, mean the rule doesn't need to fire?
+ - For example: If config changes AND install_guide.md changes, assume docs are already updated
+ - This prevents redundant prompts when the user has already done the right thing
+
+### Step 2: Choose the Detection Mode
+
+Help the user select the appropriate detection mode:
+
+**Trigger/Safety Mode** (most common):
+- Fires when trigger patterns match AND no safety patterns match
+- Use for: "When X changes, check Y" rules
+- Example: When config changes, verify install docs
+
+**Set Mode** (bidirectional correspondence):
+- Fires when files that should change together don't all change
+- Use for: Source/test pairing, model/migration sync
+- Example: `src/foo.py` and `tests/foo_test.py` should change together
+
+**Pair Mode** (directional correspondence):
+- Fires when a trigger file changes but expected files don't
+- Changes to expected files alone do NOT trigger
+- Use for: API code requires documentation updates (but docs can update independently)
+
+### Step 3: Define the Patterns
+
+Help the user define glob patterns for files.
+
+**Common patterns:**
+- `src/**/*.py` - All Python files in src directory (recursive)
+- `app/config/**/*` - All files in app/config directory
+- `*.md` - All markdown files in root
+- `src/api/**/*` - All files in the API directory
+- `migrations/**/*.sql` - All SQL migrations
+
+**Variable patterns (for set/pair modes):**
+- `src/{path}.py` - Captures path variable (e.g., `foo/bar` from `src/foo/bar.py`)
+- `tests/{path}_test.py` - Uses same path variable in corresponding file
+- `{name}` matches single segment, `{path}` matches multiple segments
+
+**Pattern syntax:**
+- `*` - Matches any characters within a single path segment
+- `**` - Matches any characters across multiple path segments (recursive)
+- `?` - Matches a single character
+
+### Step 4: Choose the Comparison Mode (Optional)
+
+The `compare_to` field controls what baseline is used when detecting "changed files":
+
+**Options:**
+- `base` (default) - Compares to the base of the current branch (merge-base with main/master). Best for feature branches.
+- `default_tip` - Compares to the current tip of the default branch. Useful for seeing difference from production.
+- `prompt` - Compares to the state at the start of each prompt. For rules about very recent changes.
+
+Most rules should use the default (`base`) and don't need to specify `compare_to`.
+
+### Step 5: Write the Instructions
+
+Create clear, actionable instructions for what the agent should do when the rule fires.
+
+**Good instructions include:**
+- What to check or review
+- What files might need updating
+- Specific actions to take
+- Quality criteria for completion
+
+**Template variables available in instructions:**
+- `{trigger_files}` - Files that triggered the rule
+- `{expected_files}` - Expected corresponding files (for set/pair modes)
+
+### Step 6: Create the Rule File
+
+Create a new file in `.deepwork/rules/` with a kebab-case filename:
+
+**File Location**: `.deepwork/rules/{rule-name}.md`
+
+**Format for Trigger/Safety Mode:**
+```markdown
+---
+name: Friendly Name for the Rule
+trigger: "glob/pattern/**/*" # or array: ["pattern1", "pattern2"]
+safety: "optional/pattern" # optional, or array
+compare_to: base # optional: "base" (default), "default_tip", or "prompt"
+---
+Instructions for the agent when this rule fires.
+
+Multi-line markdown content is supported.
+```
+
+**Format for Set Mode (bidirectional):**
+```markdown
+---
+name: Source/Test Pairing
+set:
+ - src/{path}.py
+ - tests/{path}_test.py
+---
+Source and test files should change together.
+
+Modified: {trigger_files}
+Expected: {expected_files}
+```
+
+**Format for Pair Mode (directional):**
+```markdown
+---
+name: API Documentation
+pair:
+ trigger: api/{path}.py
+ expects: docs/api/{path}.md
+---
+API code requires documentation updates.
+
+Changed API: {trigger_files}
+Update docs: {expected_files}
+```
+
+**Format for Command Action (auto-run commands):**
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies will be automatically synced when pyproject.toml changes.
+```
+
+Command actions execute automatically instead of prompting the agent. Use for:
+- Dependency syncing (`uv sync`, `npm install`)
+- Linting/formatting (`ruff format`, `prettier`)
+- Code generation tasks
+
+### Step 7: Verify the Rule
+
+After creating the rule:
+
+1. **Check the YAML frontmatter** - Ensure valid YAML formatting
+2. **Test trigger patterns** - Verify patterns match intended files
+3. **Review instructions** - Ensure they're clear and actionable
+4. **Check for conflicts** - Ensure the rule doesn't conflict with existing ones
+
+## Example Rules
+
+### Update Documentation on Config Changes
+`.deepwork/rules/config-docs.md`:
+```markdown
+---
+name: Update Install Guide on Config Changes
+trigger: app/config/**/*
+safety: docs/install_guide.md
+---
+Configuration files have been modified. Please review docs/install_guide.md
+and update it if any installation instructions need to change based on the
+new configuration.
+```
+
+### Security Review for Auth Code
+`.deepwork/rules/security-review.md`:
+```markdown
+---
+name: Security Review for Authentication Changes
+trigger:
+ - src/auth/**/*
+ - src/security/**/*
+safety:
+ - SECURITY.md
+ - docs/security_audit.md
+---
+Authentication or security code has been changed. Please:
+
+1. Review for hardcoded credentials or secrets
+2. Check input validation on user inputs
+3. Verify access control logic is correct
+4. Update security documentation if needed
+```
+
+### Source/Test Pairing
+`.deepwork/rules/source-test-pairing.md`:
+```markdown
+---
+name: Source/Test Pairing
+set:
+ - src/{path}.py
+ - tests/{path}_test.py
+---
+Source and test files should change together.
+
+When modifying source code, ensure corresponding tests are updated.
+When adding tests, ensure they test actual source code.
+
+Modified: {trigger_files}
+Expected: {expected_files}
+```
+
+### API Documentation Sync
+`.deepwork/rules/api-docs.md`:
+```markdown
+---
+name: API Documentation Update
+pair:
+ trigger: src/api/{path}.py
+ expects: docs/api/{path}.md
+---
+API code has changed. Please verify that API documentation in docs/api/
+is up to date with the code changes. Pay special attention to:
+
+- New or changed endpoints
+- Modified request/response schemas
+- Updated authentication requirements
+
+Changed API: {trigger_files}
+Update: {expected_files}
+```
+
+### Auto-Sync Dependencies (Command Action)
+`.deepwork/rules/auto-sync-deps.md`:
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies are automatically synced when pyproject.toml changes.
+```
+
+## Output Format
+
+### .deepwork/rules/{rule-name}.md
+Create a new file with the rule definition using YAML frontmatter and markdown body.
+
+## Quality Criteria
+
+- Asked structured questions to understand user requirements
+- Rule name is clear and descriptive (used in promise tags)
+- Correct detection mode selected for the use case
+- Patterns accurately match the intended files
+- Safety patterns prevent unnecessary triggering (if applicable)
+- Instructions are actionable and specific
+- YAML frontmatter is valid
+
+## Context
+
+Rules are evaluated automatically when the agent finishes a task. The system:
+1. Determines which files have changed based on each rule's `compare_to` setting
+2. Evaluates rules based on their detection mode (trigger/safety, set, or pair)
+3. Skips rules where the correspondence is satisfied (for set/pair) or safety matched
+4. Prompts you with instructions for any triggered rules
+
+You can mark a rule as addressed by including `Rule Name` in your response (replace Rule Name with the actual rule name from the `name` field). This tells the system you've already handled that rule's requirements.
+
+
+
+## Required Inputs
+
+**User Parameters** - Gather from user before starting:
+- **rule_purpose**: What guideline or constraint should this rule enforce?
+
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/rules/{rule-name}.md`
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: .deepwork/rules/{rule-name}.md"
+3. **Workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/define.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/deepwork_jobs/define.toml b/.gemini/skills/deepwork_jobs/define.toml
index 8a705168..4327a093 100644
--- a/.gemini/skills/deepwork_jobs/define.toml
+++ b/.gemini/skills/deepwork_jobs/define.toml
@@ -22,460 +22,105 @@ prompt = """
## Objective
-Create a `job.yml` specification file that defines the structure of a new DeepWork job by thoroughly understanding the user's workflow requirements through an interactive question-and-answer process.
+Create a `job.yml` specification file that defines a new DeepWork job by understanding the user's workflow requirements through interactive questioning.
## Task
-Guide the user through defining a job specification by asking structured questions. **Do not attempt to create the specification without first fully understanding the user's needs.**
+Guide the user through defining a job specification by asking structured questions. The output is **only** the `job.yml` file - step instruction files are created in the `implement` step.
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
+### Phase 1: Understand the Job Purpose
-The output of this step is **only** the `job.yml` file - a complete specification of the workflow. The actual step instruction files will be created in the next step (`implement`).
+Ask structured questions to understand the workflow:
-### Step 1: Understand the Job Purpose
+1. **Overall goal** - What complex task are they trying to accomplish? What domain (research, marketing, development, reporting)?
-Start by asking structured questions to understand what the user wants to accomplish:
+2. **Success criteria** - What's the final deliverable? Who is the audience? What quality matters most?
-1. **What is the overall goal of this workflow?**
- - What complex task are they trying to accomplish?
- - What domain is this in? (e.g., research, marketing, development, reporting)
- - How often will they run this workflow?
+3. **Major phases** - What are the distinct stages from start to finish? Any dependencies between phases?
-2. **What does success look like?**
- - What's the final deliverable or outcome?
- - Who is the audience for the output?
- - What quality criteria matter most?
+### Phase 2: Detect Document-Oriented Workflows
-3. **What are the major phases?**
- - Ask them to describe the workflow at a high level
- - What are the distinct stages from start to finish?
- - Are there any dependencies between phases?
-
-### Step 1.5: Detect Document-Oriented Workflows
-
-**Check for document-focused patterns** in the user's description:
-- Keywords: "report", "summary", "document", "create", "monthly", "quarterly", "for stakeholders", "for leadership"
-- Final deliverable is a specific document (e.g., "AWS spending report", "competitive analysis", "sprint summary")
+Check for document-focused patterns in the user's description:
+- Keywords: "report", "summary", "document", "monthly", "quarterly", "for stakeholders"
+- Final deliverable is a specific document type
- Recurring documents with consistent structure
-**If a document-oriented workflow is detected:**
-
-1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first to ensure consistent quality."
-
-2. Ask structured questions to understand if they want to:
- - Create a doc spec for this document
- - Use an existing doc spec (if any exist in `.deepwork/doc_specs/`)
- - Skip doc spec and proceed with simple outputs
-
-### Step 1.6: Define the Doc Spec (if needed)
+**If detected**, offer to create a doc spec:
+1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first."
+2. Ask if they want to create a doc spec, use existing one, or skip
-When creating a doc spec, gather the following information:
+**If creating a doc spec**, gather:
+- Document name and purpose
+- Target audience and frequency
+- Quality criteria (3-5, focused on the output document itself)
+- Document structure (sections, required elements)
-1. **Document Identity**
- - What is the document called? (e.g., "Monthly AWS Spending Report")
- - Brief description of its purpose
- - Where should these documents be stored? (path patterns like `finance/aws-reports/*.md`)
-
-2. **Audience and Context**
- - Who reads this document? (target audience)
- - How often is it produced? (frequency)
-
-3. **Quality Criteria** (3-5 criteria, each with name and description)
-
- **Important**: Doc spec quality criteria define requirements for the **output document itself**, not the process of creating it. Focus on what the finished document must contain or achieve.
-
- Examples for a spending report:
- - **Visualization**: Must include charts showing spend breakdown by service
- - **Variance Analysis**: Must compare current month against previous with percentages
- - **Action Items**: Must include recommended cost optimization actions
-
- **Note**: When a doc spec is created for a step's output, the step should generally NOT have separate `quality_criteria` in the job.yml. The doc spec's criteria cover output quality. Only add step-level quality_criteria if there are essential process requirements (e.g., "must use specific tool"), and minimize these when possible.
-
-4. **Document Structure**
- - What sections should it have?
- - Any required elements (tables, charts, summaries)?
-
-### Step 1.7: Create the doc spec File (if needed)
-
-Create the doc spec file at `.deepwork/doc_specs/[doc_spec_name].md`:
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/doc_spec.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/doc_specs/job_spec.md` for a fully worked example (the doc spec for job.yml files).
-
-After creating the doc spec, proceed to Step 2 with the doc spec reference for the final step's output.
-
-### Step 2: Define Each Step
-
-For each major phase they mentioned, ask structured questions to gather details:
-
-1. **Step Purpose**
- - What exactly does this step accomplish?
- - What is the input to this step?
- - What is the output from this step?
-
-2. **Step Inputs**
- - What information is needed to start this step?
- - Does it need user-provided parameters? (e.g., topic, target audience)
- - Does it need files from previous steps?
- - What format should inputs be in?
-
-3. **Step Outputs**
- - What files or artifacts does this step produce?
- - What format should the output be in? (markdown, YAML, JSON, etc.)
- - Where should each output be saved? (filename/path)
- - Should outputs be organized in subdirectories? (e.g., `reports/`, `data/`, `drafts/`)
- - Will other steps need this output?
- - **Does this output have a doc spec?** If a doc spec was created in Step 1.6/1.7, reference it for the appropriate output
-
- #### Work Product Storage Guidelines
-
- **Key principle**: Job outputs belong in the main repository directory structure, not in dot-directories. The `.deepwork/` directory is for job definitions and configuration only.
-
- **Why this matters**:
- - **Version control**: Work products in the main repo are tracked by git and visible in PRs
- - **Discoverability**: Team members can find outputs without knowing about DeepWork internals
- - **Tooling compatibility**: IDEs, search tools, and CI/CD work naturally with standard paths
- - **Glob patterns**: Well-structured paths enable powerful file matching (e.g., `competitive_research/**/*.md`)
-
- **Good output path patterns**:
- ```
- competitive_research/competitors_list.md
- competitive_research/acme_corp/research.md
- operations/reports/2026-01/spending_analysis.md
- docs/api/endpoints.md
- ```
-
- **Avoid these patterns**:
- ```
- .deepwork/outputs/report.md # Hidden in dot-directory
- output.md # Too generic, no context
- research.md # Unclear which research
- temp/draft.md # Transient-sounding paths
- ```
-
- **Organizing multi-file outputs**:
- - Use the job name as a top-level folder when outputs are job-specific
- - Use parameterized paths for per-entity outputs: `competitive_research/[competitor_name]/`
- - Match existing project conventions when extending a codebase
-
- **When to include dates in paths**:
- - **Include date** for periodic outputs where each version is retained (e.g., monthly reports, quarterly reviews, weekly summaries). These accumulate over time and historical versions remain useful.
- ```
- operations/reports/2026-01/spending_analysis.md # Monthly report - keep history
- hr/employees/[employee_name]/quarterly_reviews/2026-Q1.pdf # Per-employee quarterly review
- ```
- - **Omit date** for current-state outputs that represent the latest understanding and get updated in place. Previous versions live in git history, not separate files.
- ```
- competitive_research/acme_corp/swot.md # Current SWOT - updated over time
- docs/architecture/overview.md # Living document
- ```
-
- **Supporting materials and intermediate outputs**:
- - Content generated in earlier steps to support the final output (research notes, data extracts, drafts) should be placed in a `_dataroom` folder that is a peer to the final output
- - Name the dataroom folder by replacing the file extension with `_dataroom`
- ```
- operations/reports/2026-01/spending_analysis.md # Final output
- operations/reports/2026-01/spending_analysis_dataroom/ # Supporting materials
- raw_data.csv
- vendor_breakdown.md
- notes.md
- ```
- - This keeps supporting materials organized and discoverable without cluttering the main output location
-
-4. **Step Dependencies**
- - Which previous steps must complete before this one?
- - Are there any ordering constraints?
-
-5. **Step Process** (high-level understanding)
- - What are the key activities in this step?
- - Are there any quality checks or validation needed?
- - What makes a good vs. bad output for this step?
-
-6. **Agent Delegation** (optional)
- - Should this step be executed by a specific agent type?
- - Use the `agent` field when the step should run in a forked context with a specific agent
- - When `agent` is set, the generated skill automatically includes `context: fork`
- - Available agent types:
- - `general-purpose` - Standard agent for multi-step tasks
-
- ```yaml
- steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
- ```
-
-**Note**: You're gathering this information to understand what instructions will be needed, but you won't create the instruction files yet - that happens in the `implement` step.
-
-#### Doc Spec-Aware Output Format
-
-When a step produces a document with a doc spec reference, use this format in job.yml:
-
-```yaml
-outputs:
- - file: reports/monthly_spending.md
- doc_spec: .deepwork/doc_specs/monthly_aws_report.md
-```
+Create at `.deepwork/doc_specs/[doc_spec_name].md`. Reference `.deepwork/doc_specs/job_spec.md` for an example.
-The doc spec's quality criteria will automatically be included in the generated skill, ensuring consistent document quality.
+### Phase 3: Define Each Step
-### Capability Considerations
+For each major phase, gather:
-When defining steps, identify any that require specialized tools:
+1. **Purpose** - What does this step accomplish? What are inputs and outputs?
-**Browser Automation**: If any step involves web scraping, form filling, interactive browsing, UI testing, or research requiring website visits, ask the user what browser tools they have available. For Claude Code users, **Claude in Chrome** (Anthropic's browser extension) has been tested with DeepWork and is recommended for new users. Don't assume a default—confirm the tool before designing browser-dependent steps.
+2. **Inputs**:
+ - User-provided parameters (e.g., topic, target audience)?
+ - Files from previous steps?
-### Step 3: Validate the Workflow
+3. **Outputs**:
+ - What files does this step produce?
+ - Format (markdown, YAML, JSON)?
+ - Where to save? (Use meaningful paths like `competitive_research/analysis.md`, not `.deepwork/outputs/`)
+ - Does this output have a doc spec?
-After gathering information about all steps:
+4. **Dependencies** - Which previous steps must complete first?
-1. **Review the flow**
- - Summarize the complete workflow
- - Show how outputs from one step feed into the next
- - Ask if anything is missing
+5. **Process** - Key activities? Quality checks needed?
-2. **Check for gaps**
- - Are there any steps where the input isn't clearly defined?
- - Are there any outputs that aren't used by later steps?
- - Are there circular dependencies?
+6. **Agent Delegation** - Should this step run via a specific agent? Use `agent: experts` for domain-specific expertise.
-3. **Confirm details**
- - Job name (lowercase, underscores, descriptive)
- - Job summary (one clear sentence, max 200 chars)
- - Job description (detailed multi-line explanation)
- - Version number (start with 1.0.0)
+#### Output Path Guidelines
-### Step 4: Define Quality Validation (Stop Hooks)
+- Place outputs in main repo, not dot-directories
+- Use job name as top-level folder for job-specific outputs
+- Include dates for periodic outputs that accumulate (monthly reports)
+- Omit dates for current-state outputs that get updated in place
+- Use `_dataroom` folders for supporting materials
-For each step, consider whether it would benefit from **quality validation loops**. Stop hooks allow the AI agent to iteratively refine its work until quality criteria are met.
-
-**Ask structured questions about quality validation:**
-- "Are there specific quality criteria that must be met for this step?"
-- "Would you like the agent to validate its work before completing?"
-- "What would make you send the work back for revision?"
-
-**Stop hooks are particularly valuable for:**
-- Steps with complex outputs that need multiple checks
-- Steps where quality is critical (final deliverables)
-- Steps with subjective quality criteria that benefit from AI self-review
-
-**Three types of stop hooks are supported:**
-
-1. **Inline Prompt** (`prompt`) - Best for simple quality criteria
- ```yaml
- stop_hooks:
- - prompt: |
- Verify the output meets these criteria:
- 1. Contains at least 5 competitors
- 2. Each competitor has a description
- 3. Selection rationale is clear
- ```
-
-2. **Prompt File** (`prompt_file`) - For detailed/reusable criteria
- ```yaml
- stop_hooks:
- - prompt_file: hooks/quality_check.md
- ```
-
-3. **Script** (`script`) - For programmatic validation (tests, linting)
- ```yaml
- stop_hooks:
- - script: hooks/run_tests.sh
- ```
-
-**Multiple hooks can be combined:**
-```yaml
-stop_hooks:
- - script: hooks/lint_output.sh
- - prompt: "Verify the content is comprehensive and well-organized"
-```
+### Phase 4: Validate the Workflow
-**Encourage prompt-based hooks** - They leverage the AI's ability to understand context and make nuanced quality judgments. Script hooks are best for objective checks (syntax, format, tests).
+After gathering all step information:
-### Step 5: Create the Job Directory and Specification
+1. **Review the flow** - Summarize the complete workflow, show how outputs feed into next steps
+2. **Check for gaps** - Undefined inputs? Unused outputs? Circular dependencies?
+3. **Confirm details** - Job name (lowercase_underscores), summary (max 200 chars), description (detailed), version (1.0.0)
-Only after you have complete understanding, create the job directory and `job.yml` file:
+### Phase 5: Create the Job
-**First, create the directory structure** using the `make_new_job.sh` script:
+Create the directory structure:
```bash
.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
```
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - For step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - For custom validation scripts
-- `.deepwork/jobs/[job_name]/templates/` - For example file formats
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Then create the job.yml file** at `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the NEW job you're creating, e.g., `competitive_research`)
-
-**Doc Spec**: See `.deepwork/doc_specs/job_spec.md` for the complete specification with quality criteria.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.example` for a fully worked example.
-
-**Important**:
-- Use lowercase with underscores for job name and step IDs
-- Ensure file inputs reference steps in dependencies
-- No circular dependencies
-- At least one output per step
-- The `summary` should be concise (max 200 chars)
-- The `description` should provide rich context for future refinement
-- Include a `changelog` section with an initial entry for version 1.0.0
-
-## Example Dialog
-
-```
-Claude: I'll help you define a new DeepWork job. Let's start by understanding what you're trying to accomplish.
-
-What complex task or workflow are you trying to create? What's the overall goal?
-
-User: I want to create competitive research reports for my company
-
-Claude: Great! Let's dig into that. A few questions:
-
-1. What's the final deliverable? Is it a written report, a presentation, or something else?
-2. Who is the audience? Your team, executives, clients?
-3. How comprehensive should these reports be?
-
-User: It's a written report for our product team. Should be comprehensive - analyzing 3-5 competitors.
-
-Claude: Perfect. Now let's think about the major phases of creating this report. What are the main stages you go through? For example:
-- Identifying which competitors to analyze?
-- Gathering information about them?
-- Analyzing the data?
-- Writing the report?
-
-Or is your process different?
+Create `job.yml` at `.deepwork/jobs/[job_name]/job.yml`.
-User: Yes, that's close. I would say:
-1. Identify competitors
-2. Research each one
-3. Create comparison analysis
-4. Write positioning recommendations
+**Reference**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.template` for structure and `.deepwork/jobs/deepwork_jobs/templates/job.yml.example` for a complete example.
-Claude: Excellent! Now let's define each step in detail. Starting with "Identify competitors":
-
-1. What inputs do you need to start? Do you already have a market segment in mind, or do you need to specify that?
-2. What should the output look like? A list of competitor names? More detailed?
-3. How many competitors should be identified?
-
-User: I need to specify the market segment and product category as inputs. The output should be a markdown file with 5-7 competitors and brief descriptions of each.
-
-Claude: Perfect. A few more questions about that output:
-1. What should the file be called? Something like `competitors_list.md`?
-2. Should it go in the project root, or in a subdirectory like `research/` or `data/`?
-
-User: Just call it `competitors_list.md` in the project root.
-
-Claude: Great. Now for the "Research each one" step:
-1. Does it use the competitor list from the first step as input?
-2. What kind of research - product features, pricing, marketing, all of the above?
-3. What format should the research output be in?
-4. Where should that research be saved?
-
-[continues for each step...]
-
-[After gathering all information:]
-
-Claude: Let me summarize the workflow I've designed based on our conversation:
-
-**Job: competitive_research**
-Summary: Systematic competitive analysis workflow for product positioning
-
-Description:
-A comprehensive workflow for analyzing competitors in your market segment. This job helps product teams understand the competitive landscape by systematically identifying competitors, researching their offerings, creating comparison matrices, and developing strategic positioning recommendations.
-
-The workflow produces:
-- A vetted list of key competitors
-- Detailed research notes on each competitor
-- A comparison matrix highlighting key differentiators
-- Strategic positioning recommendations
-
-Designed for product teams conducting quarterly competitive analysis.
-
-**Steps:**
-
-Step 1: identify_competitors
-- Input: market_segment (user), product_category (user)
-- Output: competitors_list.md (5-7 competitors with descriptions)
-
-Step 2: research_competitors
-- Input: competitors_list.md (from step 1)
-- Output: research_notes.md (detailed findings for each competitor)
-- Depends on: identify_competitors
-
-Step 3: comparative_analysis
-- Input: research_notes.md (from step 2)
-- Output: comparison_matrix.md (side-by-side analysis)
-- Depends on: research_competitors
-
-Step 4: positioning_recommendations
-- Input: comparison_matrix.md (from step 3)
-- Output: positioning_report.md (strategic recommendations)
-- Depends on: comparative_analysis
-
-Does this accurately capture your workflow? Any changes needed?
-
-User: Yes, that's perfect!
-
-Claude: Great! Creating the job.yml specification now...
-
-[Creates .deepwork/jobs/competitive_research/job.yml with the complete spec]
-
-✓ Job specification created!
-
-**File created:**
-- .deepwork/jobs/competitive_research/job.yml
-
-**Next step:**
-Run `/deepwork_jobs.review_job_spec` to validate the specification against quality criteria.
-```
-
-## Important Guidelines
-
-1. **Focus on specification only** - Don't create instruction files yet
-2. **Ask structured questions** - Never skip the discovery phase; use the AskUserQuestion tool
-3. **Rich context in description** - This helps with future refinement
-4. **Validate understanding** - Summarize and confirm before creating
-5. **Use examples** - Help users understand what good specifications look like
-6. **Understand file organization** - Always ask structured questions about where outputs should be saved and if subdirectories are needed
-
-## Validation Rules
-
-Before creating the job.yml, ensure:
+**Validation rules**:
- Job name: lowercase, underscores, no spaces
- Version: semantic versioning (1.0.0)
-- Summary: concise, under 200 characters
-- Description: detailed, provides context
-- Step IDs: unique, descriptive, lowercase with underscores
-- Dependencies: must reference existing step IDs
-- File inputs: `from_step` must be in dependencies
+- Summary: under 200 characters
+- Step IDs: unique, lowercase with underscores
+- Dependencies must reference existing steps
+- File inputs with `from_step` must be in dependencies
- At least one output per step
-- Outputs can be filenames (e.g., `report.md`) or paths (e.g., `reports/analysis.md`)
-- File paths in outputs should match where files will actually be created
-- No circular dependencies
-
-## Output Format
-
-### job.yml
-
-The complete YAML specification file (example shown in Step 5 above).
-
-**Location**: `.deepwork/jobs/[job_name]/job.yml`
-(Where `[job_name]` is the name of the new job being created)
+## Output
-After creating the file:
-1. Inform the user that the specification is complete
-2. Recommend that they review the job.yml file
-3. Tell them to run `/deepwork_jobs.review_job_spec` next
+**File**: `.deepwork/jobs/[job_name]/job.yml`
+After creating the file, tell the user to run `/deepwork_jobs.review_job_spec` next.
### Job Context
diff --git a/.gemini/skills/deepwork_jobs/implement.toml b/.gemini/skills/deepwork_jobs/implement.toml
index 484f4bcc..e6c02a1f 100644
--- a/.gemini/skills/deepwork_jobs/implement.toml
+++ b/.gemini/skills/deepwork_jobs/implement.toml
@@ -26,15 +26,15 @@ Before proceeding, confirm these steps are complete:
## Objective
-Generate the DeepWork job directory structure and instruction files for each step based on the validated `job.yml` specification from the review_job_spec step.
+Generate the DeepWork job directory structure and instruction files for each step based on the validated `job.yml` specification.
## Task
-Read the `job.yml` specification file and create all the necessary files to make the job functional, including directory structure and step instruction files. Then sync the commands to make them available.
+Read the `job.yml` specification and create all necessary files to make the job functional, then sync the commands.
-### Step 1: Create Directory Structure Using Script
+### Step 1: Create Directory Structure
-Run the `make_new_job.sh` script to create the standard directory structure:
+Run the `make_new_job.sh` script:
```bash
.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
@@ -43,218 +43,88 @@ Run the `make_new_job.sh` script to create the standard directory structure:
This creates:
- `.deepwork/jobs/[job_name]/` - Main job directory
- `.deepwork/jobs/[job_name]/steps/` - Step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - Custom validation scripts (with .gitkeep)
-- `.deepwork/jobs/[job_name]/templates/` - Example file formats (with .gitkeep)
+- `.deepwork/jobs/[job_name]/hooks/` - Custom validation scripts
+- `.deepwork/jobs/[job_name]/templates/` - Example file formats
- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-**Note**: If the directory already exists (e.g., job.yml was created by define step), you can skip this step or manually create the additional directories:
-```bash
-mkdir -p .deepwork/jobs/[job_name]/hooks .deepwork/jobs/[job_name]/templates
-touch .deepwork/jobs/[job_name]/hooks/.gitkeep .deepwork/jobs/[job_name]/templates/.gitkeep
-```
+**Note**: If directory exists from define step, skip or just create missing subdirectories.
### Step 2: Read and Validate the Specification
-1. **Locate the job.yml file**
- - Read `.deepwork/jobs/[job_name]/job.yml` from the review_job_spec step
- - Parse the YAML content
-
-2. **Validate the specification**
- - Ensure it follows the schema (name, version, summary, description, steps)
- - Check that all dependencies reference existing steps
- - Verify no circular dependencies
- - Confirm file inputs match dependencies
-
-3. **Extract key information**
- - Job name, version, summary, description
- - List of all steps with their details
- - Understand the workflow structure
+1. Read `.deepwork/jobs/[job_name]/job.yml`
+2. Validate: name, version, summary, description, steps are present
+3. Check dependencies reference existing steps, no circular dependencies
+4. Verify file inputs match dependencies
### Step 3: Generate Step Instruction Files
-For each step in the job.yml, create a comprehensive instruction file at `.deepwork/jobs/[job_name]/steps/[step_id].md`.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template` for the standard structure.
+For each step in job.yml, create `.deepwork/jobs/[job_name]/steps/[step_id].md`.
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example` for a fully worked example.
+**Reference**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template` for structure.
-**Available templates in `.deepwork/jobs/deepwork_jobs/templates/`:**
-- `job.yml.template` - Job specification structure
-- `step_instruction.md.template` - Step instruction file structure
-- `agents.md.template` - AGENTS.md file structure
-- `job.yml.example` - Complete job specification example
-- `step_instruction.md.example` - Complete step instruction example
+**Guidelines**:
-**Guidelines for generating instructions:**
-
-1. **Use the job description** - The detailed description from job.yml provides crucial context
-2. **Be specific** - Don't write generic instructions; tailor them to the step's purpose
+1. **Use the job description** - It provides crucial context
+2. **Be specific** - Tailor instructions to the step's purpose, not generic
3. **Provide examples** - Show what good output looks like
-4. **Explain the "why"** - Help the user understand the step's role in the workflow
-5. **Quality over quantity** - Detailed, actionable instructions are better than vague ones
-6. **Align with stop hooks** - If the step has `stop_hooks` defined, ensure the quality criteria in the instruction file match the validation criteria in the hooks
-7. **Ask structured questions** - When a step has user inputs, the instructions MUST explicitly tell the agent to "ask structured questions" using the AskUserQuestion tool to gather that information. Never use generic phrasing like "ask the user" - always use "ask structured questions"
-
-### Handling Stop Hooks
-
-If a step in the job.yml has `stop_hooks` defined, the generated instruction file should:
-
-1. **Mirror the quality criteria** - The "Quality Criteria" section should match what the stop hooks will validate
-2. **Be explicit about success** - Help the agent understand when the step is truly complete
-3. **Include the promise pattern** - Mention that `✓ Quality Criteria Met` should be included when criteria are met
-
-**Example: If the job.yml has:**
-```yaml
-- id: research_competitors
- name: "Research Competitors"
- stop_hooks:
- - prompt: |
- Verify the research meets criteria:
- 1. Each competitor has at least 3 data points
- 2. Sources are cited
- 3. Information is current (within last year)
-```
-
-**The instruction file should include:**
-```markdown
-## Quality Criteria
-
-- Each competitor has at least 3 distinct data points
-- All information is sourced with citations
-- Data is current (from within the last year)
-- When all criteria are met, include `✓ Quality Criteria Met` in your response
-```
-
-This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing.
-
-### Using Supplementary Reference Files
+4. **Explain the "why"** - Help understand the step's role in the workflow
+5. **Ask structured questions** - Steps with user inputs MUST explicitly tell the agent to "ask structured questions"
+6. **Align with hooks** - If step has `stop_hooks`, ensure quality criteria match the hooks
-Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root.
-
-See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples.
+Each instruction file should include:
+- **Objective** - What this step accomplishes
+- **Task** - Detailed process
+- **Output Format** - Examples of expected outputs
+- **Quality Criteria** - How to verify completion
### Step 4: Verify job.yml Location
-Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define and review_job_spec steps should have created and validated it. If for some reason it's not there, you may need to create or move it.
+Ensure `job.yml` is at `.deepwork/jobs/[job_name]/job.yml`.
### Step 5: Sync Skills
-Run `deepwork sync` to generate the skills for this job:
+Run:
```bash
deepwork sync
```
-This will:
-- Parse the job definition
-- Generate skills for each step
-- Make the skills available in `.claude/skills/` (or appropriate platform directory)
-
-### Step 6: Consider Rules for the New Job
+This generates skills in `.claude/skills/` (or appropriate platform directory).
-After implementing the job, consider whether there are **rules** that would help enforce quality or consistency when working with this job's domain.
+### Step 6: Consider Rules
-**What are rules?**
+After implementing, consider whether **rules** would help this job's domain.
-Rules are automated guardrails stored as markdown files in `.deepwork/rules/` that trigger when certain files change during an AI session. They help ensure:
-- Documentation stays in sync with code
+**What are rules?** Automated guardrails that trigger when certain files change, ensuring:
+- Documentation stays in sync
- Team guidelines are followed
-- Architectural decisions are respected
- Quality standards are maintained
**When to suggest rules:**
-
-Think about the job you just implemented and ask:
- Does this job produce outputs that other files depend on?
-- Are there documentation files that should be updated when this job's outputs change?
-- Are there quality checks or reviews that should happen when certain files in this domain change?
-- Could changes to the job's output files impact other parts of the project?
-
-**Examples of rules that might make sense:**
+- Are there docs that should update when outputs change?
+- Could changes impact other parts of the project?
+**Examples**:
| Job Type | Potential Rule |
|----------|----------------|
| API Design | "Update API docs when endpoint definitions change" |
-| Database Schema | "Review migrations when schema files change" |
| Competitive Research | "Update strategy docs when competitor analysis changes" |
| Feature Development | "Update changelog when feature files change" |
-| Configuration Management | "Update install guide when config files change" |
-
-**How to offer rule creation:**
-
-If you identify one or more rules that would benefit the user, explain:
-1. **What the rule would do** - What triggers it and what action it prompts
-2. **Why it would help** - How it prevents common mistakes or keeps things in sync
-3. **What files it would watch** - The trigger patterns
-Then ask the user:
+If you identify helpful rules, explain what they would do and offer: "Would you like me to create this rule? I can run `/deepwork_rules.define` to set it up."
-> "Would you like me to create this rule for you? I can run `/deepwork_rules.define` to set it up."
-
-If the user agrees, invoke the `/deepwork_rules.define` command to guide them through creating the rule.
-
-**Example dialogue:**
-
-```
-Based on the competitive_research job you just created, I noticed that when
-competitor analysis files change, it would be helpful to remind you to update
-your strategy documentation.
-
-I'd suggest a rule like:
-- **Name**: "Update strategy when competitor analysis changes"
-- **Trigger**: `**/positioning_report.md`
-- **Action**: Prompt to review and update `docs/strategy.md`
-
-Would you like me to create this rule? I can run `/deepwork_rules.define` to set it up.
-```
-
-**Note:** Not every job needs rules. Only suggest them when they would genuinely help maintain consistency or quality. Don't force rules where they don't make sense.
-
-## Example Implementation
-
-For a complete worked example showing a job.yml and corresponding step instruction file, see:
-- **Job specification**: `.deepwork/jobs/deepwork_jobs/templates/job.yml.example`
-- **Step instruction**: `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example`
-
-## Important Guidelines
-
-1. **Read the spec carefully** - Understand the job's intent from the description
-2. **Generate complete instructions** - Don't create placeholder or stub files
-3. **Maintain consistency** - Use the same structure for all step instruction files
-4. **Provide examples** - Show what good output looks like
-5. **Use context** - The job description provides valuable context for each step
-6. **Be specific** - Tailor instructions to the specific step, not generic advice
-
-## Validation Before Sync
-
-Before running `deepwork sync`, verify:
-- All directories exist
-- `job.yml` is in place
-- All step instruction files exist (one per step)
-- No file system errors
+**Note**: Not every job needs rules. Only suggest when genuinely helpful.
## Completion Checklist
-Before marking this step complete, ensure:
-- [ ] job.yml validated and copied to job directory
-- [ ] All step instruction files created
-- [ ] Each instruction file is complete and actionable
+- [ ] job.yml in correct location
+- [ ] All step instruction files created (not stubs)
+- [ ] Instructions are specific and actionable
+- [ ] Output examples provided
+- [ ] Quality criteria defined for each step
- [ ] `deepwork sync` executed successfully
-- [ ] Skills generated in platform directory
-- [ ] Considered whether rules would benefit this job (Step 6)
-- [ ] If rules suggested, offered to run `/deepwork_rules.define`
-
-## Quality Criteria
-
-- Job directory structure is correct
-- All instruction files are complete (not stubs)
-- Instructions are specific and actionable
-- Output examples are provided in each instruction file
-- Quality criteria defined for each step
-- Steps with user inputs explicitly use "ask structured questions" phrasing
-- Sync completed successfully
-- Skills available for use
-- Thoughtfully considered relevant rules for the job domain
+- [ ] Considered relevant rules for this job
### Job Context
@@ -289,20 +159,6 @@ Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
**Required outputs**:
- `steps/` (directory)
-## Quality Validation (Manual)
-
-**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
-
-**Criteria (all must be satisfied)**:
-1. **Directory Structure**: Is `.deepwork/jobs/[job_name]/` created correctly?
-2. **Complete Instructions**: Are ALL step instruction files complete (not stubs or placeholders)?
-3. **Specific & Actionable**: Are instructions tailored to each step's purpose, not generic?
-4. **Output Examples**: Does each instruction file show what good output looks like?
-5. **Quality Criteria**: Does each instruction file define quality criteria for its outputs?
-6. **Ask Structured Questions**: Do step instructions that gather user input explicitly use the phrase "ask structured questions"?
-7. **Sync Complete**: Has `deepwork sync` been run successfully?
-8. **Commands Available**: Are the slash-commands generated in `.claude/commands/`?
-9. **Rules Considered**: Has the agent thought about whether rules would benefit this job? If relevant rules were identified, did they explain them and offer to run `/deepwork_rules.define`? Not every job needs rules - only suggest when genuinely helpful.
## On Completion
1. Verify outputs are created
diff --git a/.gemini/skills/deepwork_jobs/learn.toml b/.gemini/skills/deepwork_jobs/learn.toml
index ef16944b..6d53d8f7 100644
--- a/.gemini/skills/deepwork_jobs/learn.toml
+++ b/.gemini/skills/deepwork_jobs/learn.toml
@@ -22,357 +22,116 @@ prompt = """
## Objective
-Think deeply about this task. Reflect on the current conversation to identify learnings from DeepWork job executions, improve job instructions with generalizable insights, and capture bespoke (run-specific) learnings in AGENTS.md files in the deepest common folder that would contain all work on the topic in the future.
+Reflect on the conversation to identify learnings from DeepWork job executions, improve job instructions with generalizable insights, and capture run-specific learnings in AGENTS.md files.
## Task
-Analyze the conversation history to extract learnings and improvements, then apply them appropriately:
-- **Generalizable learnings** → Update job instruction files
-- **Bespoke learnings** (specific to this run) → Add to AGENTS.md in the deepest common folder for the topic
+Analyze conversation history to extract learnings, then apply them:
+- **Generalizable learnings** -> Update job instruction files
+- **Bespoke learnings** (run-specific) -> Add to AGENTS.md in the working folder
### Step 1: Analyze Conversation for Job Executions
-1. **Scan the conversation** for DeepWork slash commands that were run
- - Look for patterns like `/job_name.step_id`
- - Identify which jobs and steps were executed
- - Note the order of execution
+1. **Scan the conversation** for DeepWork slash commands (`/job_name.step_id`)
+2. **Identify the target folder** - the deepest common folder for all work on the topic
+3. **If no job specified**, ask which job to learn from
-2. **Identify the target folder**
- - This should be the deepest common folder that would contain all work on the topic in the future
- - Should be clear from conversation history where work was done
- - If unclear, run `git diff` to see where changes were made on the branch
-
-3. **If no job was specified**, ask the user:
- - "Which DeepWork job would you like me to learn from?"
- - List available jobs from `.deepwork/jobs/`
-
-### Step 2: Identify Points of Confusion and Inefficiency
+### Step 2: Identify Confusion and Inefficiency
Review the conversation for:
-1. **Confusion signals**
- - Questions the agent asked that shouldn't have been necessary
- - Misunderstandings about what a step required
- - Incorrect outputs that needed correction
- - Ambiguous instructions that led to wrong interpretations
+**Confusion signals**:
+- Unnecessary questions the agent asked
+- Misunderstandings about step requirements
+- Incorrect outputs needing correction
+- Ambiguous instructions causing wrong interpretations
-2. **Inefficiency signals**
- - Extra steps or iterations that were needed
- - Information that had to be repeated
- - Context that was missing from instructions
- - Dependencies that weren't clear
+**Inefficiency signals**:
+- Extra iterations needed
+- Repeated information
+- Missing context
+- Unclear dependencies
-3. **Error patterns**
- - Failed validations and why they failed
- - Quality criteria that were misunderstood
- - Edge cases that weren't handled
+**Error patterns**:
+- Failed validations and why
+- Misunderstood quality criteria
+- Unhandled edge cases
-4. **Success patterns**
- - What worked particularly well
- - Efficient approaches worth preserving
- - Good examples that could be added to instructions
+**Success patterns**:
+- What worked well
+- Efficient approaches worth preserving
+- Good examples to add to instructions
### Step 3: Classify Learnings
-For each learning identified, determine if it is:
+For each learning, determine if it is:
-**Generalizable** (should improve instructions):
+**Generalizable** (update instructions):
- Would help ANY future run of this job
- Addresses unclear or missing guidance
-- Fixes incorrect assumptions in instructions
-- Adds helpful examples or context
-- Examples:
- - "Step instructions should mention that X format is required"
- - "Quality criteria should include checking for Y"
- - "Add example of correct output format"
-
-**doc spec-Related** (should improve doc spec files):
+- Fixes incorrect assumptions
+- Adds helpful examples
+
+**Doc spec-related** (update doc spec files):
- Improvements to document quality criteria
- Changes to document structure or format
-- Updated audience or frequency information
-- Examples:
- - "The report should include a summary table"
- - "Quality criterion 'Visualization' needs clearer requirements"
- - "Documents need a section for action items"
-**Bespoke** (should go in AGENTS.md):
+**Bespoke** (add to AGENTS.md):
- Specific to THIS project/codebase/run
-- Depends on local conventions or structure
+- Depends on local conventions
- References specific files or paths
-- Would not apply to other uses of this job
-- Examples:
- - "In this codebase, API endpoints are in `src/api/`"
- - "This project uses camelCase for function names"
- - "The main config file is at `config/settings.yml`"
-
-### Step 3.5: Identify doc spec-Related Learnings
-
-Review the conversation for doc spec-related improvements:
-
-1. **Quality Criteria Changes**
- - Were any quality criteria unclear or insufficient?
- - Did the agent repeatedly fail certain criteria?
- - Are there new criteria that should be added?
-
-2. **Document Structure Changes**
- - Did the user request different sections?
- - Were parts of the document format confusing?
- - Should the example document be updated?
-
-3. **Metadata Updates**
- - Has the target audience changed?
- - Should frequency or path patterns be updated?
-
-**Signals for doc spec improvements:**
-- User asked for changes to document format
-- Repeated validation failures on specific criteria
-- Feedback about missing sections or information
-- Changes to how documents are organized/stored
+- Would not apply to other uses
-### Step 4: Update Job Instructions (Generalizable Learnings)
+### Step 4: Update Job Instructions (Generalizable)
For each generalizable learning:
-1. **Locate the instruction file**
- - Path: `.deepwork/jobs/[job_name]/steps/[step_id].md`
-
-2. **Make targeted improvements**
- - Add missing context or clarification
+1. Locate `.deepwork/jobs/[job_name]/steps/[step_id].md`
+2. Make targeted improvements:
+ - Add missing context
- Include helpful examples
- Clarify ambiguous instructions
- - Update quality criteria if needed
-
-3. **Keep instructions concise**
- - Avoid redundancy - don't repeat the same guidance in multiple places
- - Be direct - remove verbose explanations that don't add value
- - Prefer bullet points over paragraphs where appropriate
-
-4. **Preserve instruction structure**
- - Keep existing sections (Objective, Task, Process, Output Format, Quality Criteria)
- - Add to appropriate sections rather than restructuring
- - Maintain consistency with other steps
-
-5. **Track changes for changelog**
- - Note what was changed and why
- - Prepare changelog entry for job.yml
-
-### Step 4b: Extract Shared Content into Referenced Files
-
-Review all instruction files for the job and identify content that:
-- Appears in multiple step instructions (duplicated)
-- Is lengthy and could be extracted for clarity
-- Would benefit from being maintained in one place
-
-**Extract to shared files:**
-
-1. **Create shared files** in `.deepwork/jobs/[job_name]/steps/shared/`
- - `conventions.md` - Coding/formatting conventions used across steps
- - `examples.md` - Common examples referenced by multiple steps
- - `schemas.md` - Data structures or formats used throughout
-
-2. **Reference from instructions** using markdown includes or explicit references:
- ```markdown
- ## Conventions
-
- Follow the conventions defined in `shared/conventions.md`.
- ```
-
-3. **Benefits of extraction:**
- - Single source of truth - update once, applies everywhere
- - Shorter instruction files - easier to read and maintain
- - Consistent guidance across steps
-
-### Step 4.5: Update doc spec Files (doc spec-Related Learnings)
-
-If doc spec-related learnings were identified:
-
-1. **Locate the doc spec file**
- - Find doc spec references in job.yml outputs (look for `doc_spec: .deepwork/doc_specs/[doc_spec_name].md`)
- - doc spec files are at `.deepwork/doc_specs/[doc_spec_name].md`
-
-2. **Update quality_criteria array**
- - Add new criteria with name and description
- - Modify existing criteria descriptions for clarity
- - Remove criteria that are no longer relevant
-
-3. **Update example document**
- - Modify the markdown body to reflect structure changes
- - Ensure the example matches updated criteria
-
-4. **Update metadata as needed**
- - target_audience: If audience has changed
- - frequency: If production cadence has changed
- - path_patterns: If storage location has changed
-
-**Example doc spec update:**
-```yaml
-# Before
-quality_criteria:
- - name: Visualization
- description: Include charts
-
-# After
-quality_criteria:
- - name: Visualization
- description: Include Mermaid.js charts showing spend breakdown by service and month-over-month trend
-```
-
-### Step 5: Create/Update AGENTS.md (Bespoke Learnings)
-
-The AGENTS.md file captures project-specific knowledge that helps future agent runs.
-
-1. **Determine the correct location**
- - Place AGENTS.md in the deepest common folder that would contain all work on the topic in the future
- - This ensures the knowledge is available when working in that context
- - If uncertain, place at the project root
-
-2. **Use file references where possible**
- - Instead of duplicating information, reference source files
- - This keeps AGENTS.md in sync as the codebase evolves
- - Pattern: "See `path/to/file.ext` for [description]"
+ - Update quality criteria
+3. Keep instructions concise - avoid redundancy
+4. Preserve structure (Objective, Task, Output Format, Quality Criteria)
-3. **AGENTS.md structure**: See `.deepwork/jobs/deepwork_jobs/templates/agents.md.template` for the standard format.
+### Step 5: Update Doc Specs (if applicable)
-4. **Writing entries**
- - Be concise but specific
- - Always prefer file references over inline content
- - Use line numbers when referencing specific code: `file.ext:42`
- - Group related learnings together
+If doc spec-related learnings identified:
-### Step 6: Update Job Version and Changelog
+1. Locate doc spec at `.deepwork/doc_specs/[doc_spec_name].md`
+2. Update quality_criteria, example document, or metadata as needed
-If instruction files were modified:
+### Step 6: Create/Update AGENTS.md (Bespoke)
-1. **Bump version in job.yml**
- - Patch version (0.0.x) for instruction improvements
- - Minor version (0.x.0) if quality criteria changed
+1. Place in the deepest common folder for the topic
+2. Use file references instead of duplicating content: "See `path/to/file.ext` for [description]"
+3. Follow structure in `.deepwork/jobs/deepwork_jobs/templates/agents.md.template`
-2. **Add changelog entry**
- ```yaml
- - version: "[new_version]"
- changes: "Improved [step] instructions based on execution learnings: [brief description]"
- ```
-
-### Step 7: Sync Skills
-
-**Run deepwork sync** (if instructions were modified)
-```bash
-deepwork sync
-```
-
-## File Reference Patterns
-
-When adding entries to AGENTS.md, prefer these patterns:
-
-**Good (references)**:
+**Good patterns** (references):
```markdown
- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
-- Error handling pattern: See `src/utils/errors.ts:15-30`
- Configuration schema: Defined in `config/schema.json`
```
-**Avoid (duplicating)**:
+**Avoid** (duplicating):
```markdown
- API endpoints should return JSON with this format: { status: ..., data: ... }
-- Error handling should use try/catch with custom error classes
-- Configuration requires these fields: name, version, ...
```
-## Quality Criteria
-
-- Conversation has been analyzed for job executions
-- Points of confusion and inefficiency are identified
-- Learnings are correctly classified (generalizable vs bespoke)
-- Job instructions updated for generalizable improvements
-- Instructions are concise - no redundancy or unnecessary verbosity
-- Shared/lengthy content extracted into referenced files where appropriate
-- AGENTS.md created/updated with bespoke learnings
-- File references used instead of duplicating content
-- AGENTS.md is in the correct folder (the deepest common folder for the topic)
-- When all criteria are met, include `✓ Quality Criteria Met`
-
-## Example Dialog
-
-```
-User: /deepwork_jobs.learn
-
-Claude: I'll analyze this conversation for learnings from DeepWork job executions.
-
-**Conversation Analysis**
-
-I found the following job executions:
-1. `/competitive_research.identify_competitors` - Completed
-2. `/competitive_research.research_competitors` - Completed with 2 iterations
-3. `/competitive_research.comparative_analysis` - Completed
-
-**Identified Issues**
-
-1. **Confusion in step 2**: The instructions didn't specify that competitor research should include pricing information. This caused an extra iteration.
-
-2. **Missing context**: The agent had to ask about the industry focus, which should have been carried forward from step 1.
-
-**Classification**
-
-- Generalizable: Add pricing to research requirements in instructions
-- Bespoke: This project focuses on B2B SaaS in the HR tech space
-
-**Actions Taken**
-
-1. Updated `.deepwork/jobs/competitive_research/steps/research_competitors.md`:
- - Added "Include pricing information (tiers, models, approximate ranges)" to research requirements
- - Added note about carrying forward industry context from previous step
-
-2. Created `./competitive_research_output/AGENTS.md`:
- ```markdown
- # Project Context for Competitive Research
-
- ## Job-Specific Context
-
- ### competitive_research
-
- #### Industry Focus
- - Domain: B2B SaaS, HR Technology sector
- - See `identify_competitors_output.md` for market definition
-
- #### Competitors Identified
- - Reference: `competitor_list.md` for full details
-
- ## Last Updated
- - Date: 2024-01-15
- - From conversation about: Initial competitive analysis run
- ```
-
-3. Updated job.yml version to 1.0.1 with changelog entry
-
-4. Ran `deepwork sync`
-
-**Summary**
-
-Updated job instructions and created AGENTS.md with bespoke learnings.
-```
-
-## Handling Edge Cases
-
-**No job executions found**:
-- Ask user which job they'd like to analyze
-- Or offer to review available jobs
+### Step 7: Update Job Version and Sync
-**Multiple jobs executed**:
-- Analyze each job separately
-- Create separate AGENTS.md entries or files as appropriate
+If instructions were modified:
-**AGENTS.md already exists**:
-- Read existing content
-- Append new learnings to appropriate sections
-- Update "Last Updated" section
+1. Bump version in job.yml (patch for improvements, minor for quality criteria changes)
+2. Add changelog entry
+3. Run `deepwork sync`
-**No issues found**:
-- Document what worked well
-- Consider if any successful patterns should be added to instructions as examples
+## Output
-**Sensitive information**:
-- Never include secrets, credentials, or PII in AGENTS.md
-- Reference config files instead of including values
+- Updated job instructions (generalizable learnings)
+- Updated doc specs (if applicable)
+- AGENTS.md with bespoke learnings in the correct working folder
### Job Context
@@ -407,23 +166,6 @@ Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
**Required outputs**:
- `AGENTS.md`
-## Quality Validation (Manual)
-
-**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
-
-**Criteria (all must be satisfied)**:
-1. **Conversation Analyzed**: Did the agent review the conversation for DeepWork job executions?
-2. **Confusion Identified**: Did the agent identify points of confusion, errors, or inefficiencies?
-3. **Instructions Improved**: Were job instructions updated to address identified issues?
-4. **Instructions Concise**: Are instructions free of redundancy and unnecessary verbosity?
-5. **Shared Content Extracted**: Is lengthy/duplicated content extracted into referenced files?
-6. **doc spec Reviewed (if applicable)**: For jobs with doc spec outputs, were doc spec-related learnings identified?
-7. **doc spec Updated (if applicable)**: Were doc spec files updated with improved quality criteria or structure?
-8. **Bespoke Learnings Captured**: Were run-specific learnings added to AGENTS.md?
-9. **File References Used**: Do AGENTS.md entries reference other files where appropriate?
-10. **Working Folder Correct**: Is AGENTS.md in the correct working folder for the job?
-11. **Generalizable Separated**: Are generalizable improvements in instructions, not AGENTS.md?
-12. **Sync Complete**: Has `deepwork sync` been run if instructions were modified?
## On Completion
1. Verify outputs are created
diff --git a/.gemini/skills/deepwork_jobs/review_job_spec.toml b/.gemini/skills/deepwork_jobs/review_job_spec.toml
index 265eb151..db693323 100644
--- a/.gemini/skills/deepwork_jobs/review_job_spec.toml
+++ b/.gemini/skills/deepwork_jobs/review_job_spec.toml
@@ -26,210 +26,80 @@ Before proceeding, confirm these steps are complete:
## Objective
-Review the `job.yml` created in the define step against the doc spec quality criteria using a sub-agent for unbiased evaluation, then iterate on fixes until all criteria pass.
+Review the `job.yml` created in the define step against doc spec quality criteria, then iterate on fixes until all criteria pass.
## Why This Step Exists
-The define step focuses on understanding user requirements and creating a job specification. This review step ensures the specification meets quality standards before implementation. Using a sub-agent provides an unbiased "fresh eyes" review that catches issues the main agent might miss after being deeply involved in the definition process.
+The define step focuses on understanding user requirements. This review step ensures the specification meets quality standards before implementation. A fresh review catches issues that might be missed after being deeply involved in definition.
## Task
-Use a sub-agent to review the job.yml against all 9 doc spec quality criteria, then fix any failed criteria. Repeat until all criteria pass.
+Review the job.yml against all 9 doc spec quality criteria, fix any failures, and repeat until all pass.
-### Step 1: Read the Job Specification
-
-Read the `job.yml` file created in the define step:
+### Step 1: Read the Files
+Read the job specification:
```
.deepwork/jobs/[job_name]/job.yml
```
-Also read the doc spec for reference:
-
+Read the doc spec for reference:
```
.deepwork/doc_specs/job_spec.md
```
-### Step 2: Spawn Review Sub-Agent
-
-Use the Task tool to spawn a sub-agent that will provide an unbiased review:
-
-```
-Task tool parameters:
-- subagent_type: "general-purpose"
-- model: "haiku"
-- description: "Review job.yml against doc spec"
-- prompt: [see below]
-```
-
-**Sub-agent prompt template:**
-
-```
-Review this job.yml against the following 9 quality criteria from the doc spec.
-
-For each criterion, respond with:
-- PASS or FAIL
-- If FAIL: specific issue and suggested fix
-
-## job.yml Content
-
-[paste the full job.yml content here]
-
-## Quality Criteria
-
-1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
-
-2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
-
-3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
-
-4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
-
-5. **Changelog Present**: Must include a changelog array with at least the initial version entry
-
-6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
-
-7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
-
-8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
-
-9. **Output Paths**: Outputs must be valid filenames or paths (e.g., `report.md` or `reports/analysis.md`)
-
-## Response Format
-
-Respond with a structured evaluation:
-
-### Overall: [X/9 PASS]
-
-### Criterion Results
-
-1. Valid Identifier: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-2. Semantic Version: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-[... continue for all 9 criteria ...]
-
-### Summary of Required Fixes
-
-[List any fixes needed, or "No fixes required - all criteria pass"]
-```
-
-### Step 3: Review Sub-Agent Findings
+### Step 2: Evaluate Against Quality Criteria
-Parse the sub-agent's response:
+Review the job.yml against these 9 criteria:
-1. **Count passing criteria** - How many of the 9 criteria passed?
-2. **Identify failures** - List specific criteria that failed
-3. **Note suggested fixes** - What changes does the sub-agent recommend?
+1. **Valid Identifier** - Job name is lowercase with underscores, no spaces or special characters
+2. **Semantic Version** - Version follows X.Y.Z format (e.g., `1.0.0`)
+3. **Concise Summary** - Summary is under 200 characters and clearly describes the job
+4. **Rich Description** - Description is multi-line and explains: problem solved, process, expected outcomes, target users
+5. **Changelog Present** - Includes changelog array with at least initial version entry
+6. **Complete Steps** - Each step has: id, name, description, instructions_file, outputs, dependencies
+7. **Valid Dependencies** - Dependencies reference existing step IDs with no circular references
+8. **Input Consistency** - File inputs with `from_step` reference a step in the dependencies array
+9. **Output Paths** - Outputs are valid filenames or paths
-### Step 4: Fix Failed Criteria
+For each criterion, determine PASS or FAIL. If FAIL, note the specific issue and fix.
-For each failed criterion, edit the job.yml to address the issue:
+### Step 3: Fix Failed Criteria
-**Common fixes by criterion:**
+For each failed criterion, edit the job.yml:
| Criterion | Common Issue | Fix |
|-----------|-------------|-----|
| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
-| Semantic Version | Missing or invalid format | Set to `"1.0.0"` or fix format |
-| Concise Summary | Too long or vague | Shorten to <200 chars, be specific |
-| Rich Description | Single line or missing context | Add multi-line explanation with problem/process/outcome/users |
-| Changelog Present | Missing changelog | Add `changelog:` with initial version entry |
-| Complete Steps | Missing required fields | Add id, name, description, instructions_file, outputs, dependencies |
-| Valid Dependencies | Non-existent step or circular | Fix step ID reference or reorder dependencies |
-| Input Consistency | from_step not in dependencies | Add the referenced step to dependencies array |
-| Output Paths | Invalid characters or format | Use valid filename/path format |
+| Semantic Version | Invalid format | Set to `"1.0.0"` |
+| Concise Summary | Too long | Shorten to <200 chars |
+| Rich Description | Single line | Add multi-line explanation |
+| Changelog Present | Missing | Add changelog with initial version |
+| Complete Steps | Missing fields | Add required fields |
+| Valid Dependencies | Non-existent step | Fix step ID reference |
+| Input Consistency | from_step not in deps | Add step to dependencies |
+| Output Paths | Invalid format | Use valid filename/path |
-### Step 5: Re-Run Review (If Needed)
+### Step 4: Re-Evaluate (If Needed)
If any criteria failed:
+1. Review the updated job.yml
+2. Re-evaluate all criteria
+3. Fix remaining issues
+4. Repeat until all 9 pass
-1. **Spawn a new sub-agent** with the updated job.yml content
-2. **Review the new findings**
-3. **Fix any remaining issues**
-4. **Repeat until all 9 criteria pass**
-
-### Step 6: Confirm Completion
+### Step 5: Confirm Completion
When all 9 criteria pass:
-1. **Announce success**: "All 9 doc spec quality criteria pass."
-2. **List what was validated**:
- - Valid Identifier
- - Semantic Version
- - Concise Summary
- - Rich Description
- - Changelog Present
- - Complete Steps
- - Valid Dependencies
- - Input Consistency
- - Output Paths
-3. **Include the promise**: `Quality Criteria Met`
-4. **Guide to next step**: "Run `/deepwork_jobs.implement` to generate the step instruction files."
-
-## Example Review Cycle
-
-```
-Claude: Let me review the job.yml against the doc spec quality criteria.
-
-[Spawns sub-agent with haiku model]
-
-Sub-agent response:
-### Overall: 7/9 PASS
-
-### Criterion Results
-1. Valid Identifier: PASS
-2. Semantic Version: PASS
-3. Concise Summary: FAIL
- Issue: Summary is 245 characters, exceeds 200 char limit
- Fix: Shorten to "Competitive analysis workflow for product positioning"
-4. Rich Description: PASS
-5. Changelog Present: FAIL
- Issue: No changelog array present
- Fix: Add changelog with initial version entry
-6. Complete Steps: PASS
-7. Valid Dependencies: PASS
-8. Input Consistency: PASS
-9. Output Paths: PASS
-
-### Summary of Required Fixes
-1. Shorten summary to under 200 characters
-2. Add changelog array with initial version
-
-Claude: I found 2 issues. Let me fix them.
-
-[Edits job.yml to shorten summary and add changelog]
-
-Claude: Let me verify the fixes with another review.
-
-[Spawns new sub-agent]
-
-Sub-agent response:
-### Overall: 9/9 PASS
-
-### Summary of Required Fixes
-No fixes required - all criteria pass
-
-Claude: All 9 doc spec quality criteria pass.
-
-Quality Criteria Met
-
-**Next step:** Run `/deepwork_jobs.implement` to generate the step instruction files.
-```
-
-## Quality Criteria
-
-- **Sub-Agent Used**: A sub-agent was spawned to provide unbiased review (not just self-review)
-- **All doc spec Criteria Evaluated**: The sub-agent assessed all 9 quality criteria from the doc spec
-- **Findings Addressed**: All failed criteria were fixed by the main agent
-- **Validation Loop Complete**: The review-fix cycle continued until all criteria passed
-- **Promise Included**: The response includes `Quality Criteria Met` when complete
+1. Announce: "All 9 doc spec quality criteria pass."
+2. List what was validated
+3. Include: `Quality Criteria Met`
+4. Guide to next step: "Run `/deepwork_jobs.implement` to generate the step instruction files."
## Output
-The validated `job.yml` file at `.deepwork/jobs/[job_name]/job.yml` that passes all 9 doc spec quality criteria.
+The validated `job.yml` file at `.deepwork/jobs/[job_name]/job.yml` that passes all 9 quality criteria.
### Job Context
@@ -279,15 +149,6 @@ Use branch format: `deepwork/deepwork_jobs-[instance]-YYYYMMDD`
9. **Output Paths**: Outputs must be valid filenames or paths within the main repo directory structure, never in dot-directories like `.deepwork/`. Use specific, descriptive paths that lend themselves to glob patterns (e.g., `competitive_research/acme_corp/swot.md` or `operations/reports/2026-01/spending_analysis.md`). Parameterized paths like `[competitor_name]/` are encouraged for per-entity outputs. Avoid generic names (`output.md`, `analysis.md`) and transient-sounding paths (`temp/`, `draft.md`). Supporting materials for a final output should go in a peer `_dataroom` folder (e.g., `spending_analysis_dataroom/`).
10. **Concise Instructions**: The content of the file, particularly the description, must not have excessively redundant information. It should be concise and to the point given that extra tokens will confuse the AI.
-## Quality Validation (Manual)
-
-**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
-
-**Criteria (all must be satisfied)**:
-1. **Sub-Agent Used**: Was a sub-agent spawned to provide unbiased review?
-2. **All doc spec Criteria Evaluated**: Did the sub-agent assess all 9 quality criteria?
-3. **Findings Addressed**: Were all failed criteria addressed by the main agent?
-4. **Validation Loop Complete**: Did the review-fix cycle continue until all criteria passed?
## On Completion
1. Verify outputs are created
diff --git a/.gemini/skills/deepwork_rules/define.toml b/.gemini/skills/deepwork_rules/define.toml
index 980ad931..6e4fd3b0 100644
--- a/.gemini/skills/deepwork_rules/define.toml
+++ b/.gemini/skills/deepwork_rules/define.toml
@@ -28,7 +28,7 @@ Create a new rule file in the `.deepwork/rules/` directory to enforce team guide
Guide the user through defining a new rule by asking structured questions. **Do not create the rule without first understanding what they want to enforce.**
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
### Step 1: Understand the Rule Purpose
diff --git a/.gemini/skills/experts.check_relevance/SKILL.md b/.gemini/skills/experts.check_relevance/SKILL.md
new file mode 100644
index 00000000..8151f0f0
--- /dev/null
+++ b/.gemini/skills/experts.check_relevance/SKILL.md
@@ -0,0 +1,167 @@
+# :check_relevance
+#
+# Invoke all experts in parallel to assess if PR changes are relevant to their domain
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Invoke all experts in parallel to assess if PR changes are relevant to their domain"
+
+prompt = """
+# :check_relevance
+
+**Step /** in **** workflow
+
+>
+
+
+## Instructions
+
+**Goal**: Invoke all experts in parallel to assess if PR changes are relevant to their domain
+
+# Check Expert Relevance
+
+## Objective
+
+Invoke all available experts in parallel to determine which ones have relevant expertise for the PR changes. This filtering step ensures only applicable experts spend time on detailed review.
+
+## Task
+
+Assess which domain experts can meaningfully contribute to reviewing the current PR by having each expert examine the changes and report their relevance.
+
+### Process
+
+1. **Get PR metadata**
+
+ Get basic PR info for the output file:
+ ```bash
+ gh pr view --json number,title,headRefName
+ ```
+
+ This is needed for the output file header. The diff and file list will be
+ embedded directly in expert prompts using inline command expansion.
+
+2. **Discover available experts**
+
+ List all experts in the project:
+ ```bash
+ ls -1 .deepwork/experts/
+ ```
+
+ Read each expert's `discovery_description` from their `expert.yml` to understand their domain.
+
+3. **Invoke experts in parallel**
+
+ For each expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Use `$(command)` syntax in the prompt to embed command output directly when
+ the sub-agent is spawned. This avoids reading large diffs into the main
+ conversation context.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Review this PR to determine if the changes are relevant to your domain of expertise.
+
+ ## Changed Files
+
+ $(gh pr diff --name-only)
+
+ ## Diff Summary (first 200 lines)
+
+ $(gh pr diff | head -200)
+
+ ## Your Task
+
+ Based on your specific domain knowledge, respond with:
+ 1. RELEVANT or NOT_RELEVANT
+ 2. Brief justification (1-2 sentences) explaining why this does or does not fall within your expertise
+ 3. If relevant, which specific files/changes you can review from your expert perspective
+ ```
+
+ **Important**: Invoke ALL experts in parallel to minimize latency.
+
+4. **Collect and summarize responses**
+
+ Wait for all expert responses. Compile into the output file.
+
+## Output Format
+
+### pr_review/relevance_assessments.md
+
+Create this file with the assessment results:
+
+```markdown
+# PR Relevance Assessment
+
+**PR**: #[number] - [title]
+**Branch**: [branch_name]
+**Date**: [YYYY-MM-DD]
+
+## Changed Files
+
+- path/to/file1.py
+- path/to/file2.ts
+- ...
+
+## Expert Assessments
+
+### [expert-name-1]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+### [expert-name-2]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+...
+
+## Summary
+
+**Relevant experts**: [list of expert names marked RELEVANT]
+**Next step**: Run `/experts.deep_review` with these experts
+```
+
+## Quality Criteria
+
+- All experts in `.deepwork/experts/` were invoked in parallel
+- Each expert provided a yes/no relevance determination with justification
+- Relevant experts are clearly identified for the next step
+- PR metadata (number, title, branch) is captured
+- Changed files are listed for reference
+
+## Context
+
+This is the first phase of the expert-driven PR review workflow. By checking relevance first, we avoid wasting expert review time on changes outside their domain. The parallel invocation ensures this filtering step completes quickly even with many experts.
+
+The output file serves as input to the `deep_review` step, which will only invoke experts marked as RELEVANT.
+
+
+
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/relevance_assessments.md`
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: pr_review/relevance_assessments.md"
+3. **Tell user next command**: `/:deep_review`
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/check_relevance.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.deep_review/SKILL.md b/.gemini/skills/experts.deep_review/SKILL.md
new file mode 100644
index 00000000..edd69d3a
--- /dev/null
+++ b/.gemini/skills/experts.deep_review/SKILL.md
@@ -0,0 +1,248 @@
+# :deep_review
+#
+# Invoke only relevant experts to perform detailed code review
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Invoke only relevant experts to perform detailed code review"
+
+prompt = """
+# :deep_review
+
+**Step /** in **** workflow
+
+>
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/:check_relevance`
+
+## Instructions
+
+**Goal**: Invoke only relevant experts to perform detailed code review
+
+# Deep Expert Review
+
+## Objective
+
+Have relevant domain experts perform thorough code review of the PR changes **specifically within their area of expertise**. Each expert produces detailed written feedback with specific suggestions for improvement, drawing on their domain knowledge.
+
+## Task
+
+Invoke only the experts marked as RELEVANT in the previous step to perform detailed code review, producing actionable feedback focused on their specific domain.
+
+### Process
+
+1. **Read the relevance assessments**
+
+ Read `pr_review/relevance_assessments.md` to identify:
+ - Which experts were marked RELEVANT
+ - Which specific files each expert identified as relevant to their domain
+ - The PR number and branch
+
+2. **Invoke relevant experts for deep review**
+
+ For each RELEVANT expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Instead of reading the diff yourself and passing it to each expert, use `$(command)`
+ syntax in the prompt. This embeds the command output directly when the sub-agent
+ is spawned, avoiding token overhead in the main conversation.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Perform a detailed code review of these PR changes **focusing specifically on your domain of expertise**.
+
+ IMPORTANT: Only comment on aspects that fall within your area of expertise. Do not provide
+ general code review feedback on things outside your domain - other experts will cover those areas.
+ Use your specialized knowledge to identify issues that a generalist reviewer might miss.
+
+ Your domain: [brief description from discovery_description]
+
+ Files you identified as relevant to your domain:
+ [list from relevance assessment]
+
+ ## PR Diff
+
+ $(gh pr diff)
+
+ ## Your Task
+
+ Review the diff above, focusing ONLY on files and changes relevant to your domain.
+ Ignore changes outside your expertise - other experts will cover those.
+
+ From your expert perspective, provide your review in this format:
+
+ ## Summary
+ Overall assessment of the changes as they relate to your domain (1-2 paragraphs).
+ What is this PR doing in terms of your area of expertise?
+
+ ## Issues Found
+ For each issue within your domain:
+ - **File**: path/to/file
+ - **Line(s)**: [line numbers]
+ - **Severity**: Critical / Major / Minor / Suggestion
+ - **Issue**: Description of the problem from your expert perspective
+ - **Suggestion**: How to fix it, drawing on your domain knowledge
+
+ ## Code Suggestions
+ Specific code changes you recommend based on your expertise (include before/after snippets).
+ Explain why this change is better from your domain's perspective.
+
+ ## Approval Status
+ - APPROVED: No blocking issues within your domain
+ - CHANGES_REQUESTED: Blocking issues in your domain must be addressed
+ - COMMENTS: Suggestions only within your domain, no blockers
+ ```
+
+ **Note**: Invoke relevant experts in parallel for efficiency.
+
+3. **Collect expert reviews**
+
+ Wait for all expert reviews to complete. Save each to their dedicated review file.
+
+4. **Create consolidated summary**
+
+ After all reviews complete, summarize across experts:
+ - Total issues by severity
+ - Key themes across reviews
+ - Overall approval status
+
+## Output Format
+
+### pr_review/[expert_name]/review.md
+
+Create one file per relevant expert:
+
+```markdown
+# [Expert Name] Review
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+**Reviewer**: [expert-name] expert
+
+## Summary
+
+[Expert's overall assessment from their domain perspective]
+
+## Issues Found
+
+### Issue 1
+- **File**: path/to/file.py
+- **Line(s)**: 45-52
+- **Severity**: Major
+- **Issue**: [Description from expert perspective]
+- **Suggestion**: [How to fix, using domain knowledge]
+
+### Issue 2
+...
+
+## Code Suggestions
+
+### Suggestion 1: [Brief title]
+
+**File**: path/to/file.py
+
+Before:
+```python
+# problematic code
+```
+
+After:
+```python
+# suggested improvement
+```
+
+**Rationale**: [Why this change improves the code from this expert's perspective]
+
+...
+
+## Approval Status
+
+[APPROVED / CHANGES_REQUESTED / COMMENTS]
+
+[Additional notes on approval status]
+```
+
+### pr_review/review_summary.md (optional but recommended)
+
+```markdown
+# PR Review Summary
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+
+## Expert Reviews
+
+| Expert | Domain | Status | Critical | Major | Minor |
+|--------|--------|--------|----------|-------|-------|
+| [name] | [domain] | CHANGES_REQUESTED | 0 | 2 | 3 |
+| [name] | [domain] | APPROVED | 0 | 0 | 1 |
+
+## Key Themes
+
+- [Theme 1 appearing across multiple reviews]
+- [Theme 2]
+
+## Overall Status
+
+[CHANGES_REQUESTED if any expert requested changes, otherwise APPROVED]
+
+## Next Steps
+
+[If changes requested]: Run `/experts.improve_and_rereview` to address feedback
+[If approved]: PR is ready to merge
+```
+
+## Quality Criteria
+
+- Only experts marked as relevant were invoked
+- Each expert produced written feedback in their review file
+- Feedback is focused on each expert's specific domain of expertise
+- Specific code change suggestions are included where applicable
+- Issues include file, line numbers, severity, and suggestions
+- Approval status is clearly stated by each expert
+
+## Context
+
+This is the second phase of the expert-driven PR review workflow. Deep review only happens after relevance filtering to ensure expert time is well-spent. Each expert reviews independently from their unique domain perspective, bringing specialized knowledge to identify issues that generalist reviewers might miss.
+
+The key to effective expert review is **focus**: each expert should only comment on aspects within their domain, trusting that other experts will cover other areas. This produces higher-quality, more actionable feedback than generic reviews.
+
+The review files serve as input to the `improve_and_rereview` step if changes are requested. If all experts approve, the PR can proceed to merge.
+
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `pr_review/relevance_assessments.md` (from `check_relevance`)
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/{expert_name}/review.md`
+- `pr_review/review_summary.md`
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: pr_review/{expert_name}/review.md, pr_review/review_summary.md"
+3. **Tell user next command**: `/:improve_and_rereview`
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/deep_review.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.define/SKILL.md b/.gemini/skills/experts.define/SKILL.md
new file mode 100644
index 00000000..a9051291
--- /dev/null
+++ b/.gemini/skills/experts.define/SKILL.md
@@ -0,0 +1,155 @@
+# :define
+#
+# Create a workflow.yml by gathering requirements through structured questions
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Create a workflow.yml by gathering requirements through structured questions"
+
+prompt = """
+# :define
+
+**Step /** in **** workflow
+
+>
+
+
+## Instructions
+
+**Goal**: Create a workflow.yml by gathering requirements through structured questions
+
+# Define Workflow Specification
+
+## Objective
+
+Create a `workflow.yml` specification file that defines a new DeepWork workflow by understanding the user's requirements through interactive questioning.
+
+## Task
+
+Guide the user through defining a workflow specification by asking structured questions. The output is **only** the `workflow.yml` file - step instruction files are created in the `implement` step.
+
+### Phase 1: Understand the Workflow Purpose
+
+Ask structured questions to understand the workflow:
+
+1. **Overall goal** - What complex task are they trying to accomplish? What domain (research, marketing, development, reporting)?
+
+2. **Success criteria** - What's the final deliverable? Who is the audience? What quality matters most?
+
+3. **Major phases** - What are the distinct stages from start to finish? Any dependencies between phases?
+
+4. **Expert context** - Which expert should this workflow belong to? Existing one or create new?
+
+### Phase 2: Detect Document-Oriented Workflows
+
+Check for document-focused patterns in the user's description:
+- Keywords: "report", "summary", "document", "monthly", "quarterly", "for stakeholders"
+- Final deliverable is a specific document type
+- Recurring documents with consistent structure
+
+**If detected**, offer to create a doc spec:
+1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first."
+2. Ask if they want to create a doc spec, use existing one, or skip
+
+**If creating a doc spec**, gather:
+- Document name and purpose
+- Target audience and frequency
+- Quality criteria (3-5, focused on the output document itself)
+- Document structure (sections, required elements)
+
+Create at `.deepwork/doc_specs/[doc_spec_name].md`.
+
+### Phase 3: Define Each Step
+
+For each major phase, gather:
+
+1. **Purpose** - What does this step accomplish? What are inputs and outputs?
+
+2. **Inputs**:
+ - User-provided parameters (e.g., topic, target audience)?
+ - Files from previous steps?
+
+3. **Outputs**:
+ - What files does this step produce?
+ - Format (markdown, YAML, JSON)?
+ - Where to save? (Use meaningful paths like `competitive_research/analysis.md`)
+ - Does this output have a doc spec?
+
+4. **Dependencies** - Which previous steps must complete first?
+
+5. **Process** - Key activities? Quality checks needed?
+
+6. **Agent Delegation** - Should this step run via a specific expert? Use `agent: experts` for domain-specific expertise.
+
+#### Output Path Guidelines
+
+- Place outputs in main repo, not dot-directories
+- Use workflow name as top-level folder for workflow-specific outputs
+- Include dates for periodic outputs that accumulate (monthly reports)
+- Omit dates for current-state outputs that get updated in place
+- Use `_dataroom` folders for supporting materials
+
+### Phase 4: Validate the Workflow
+
+After gathering all step information:
+
+1. **Review the flow** - Summarize the complete workflow, show how outputs feed into next steps
+2. **Check for gaps** - Undefined inputs? Unused outputs? Circular dependencies?
+3. **Confirm details** - Workflow name (lowercase_underscores), summary (max 200 chars), description (detailed), version (1.0.0)
+4. **Step ID uniqueness** - Step IDs must be unique within the expert, across all workflows
+
+### Phase 5: Create the Workflow
+
+Determine the expert and create the directory structure:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/steps
+```
+
+Create `workflow.yml` at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+**Validation rules**:
+- Workflow name: lowercase, underscores, no spaces (must match folder name)
+- Version: semantic versioning (1.0.0)
+- Summary: under 200 characters
+- Step IDs: unique within the expert, lowercase with underscores
+- Dependencies must reference existing steps
+- File inputs with `from_step` must be in dependencies
+- At least one output per step
+
+## Output
+
+**File**: `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+
+After creating the file, the next step will review it against quality criteria.
+
+
+
+## Required Inputs
+
+**User Parameters** - Gather from user before starting:
+- **workflow_purpose**: What complex task are you trying to accomplish?
+
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml`
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+3. **Tell user next command**: `/:review_workflow_spec`
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/define.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.implement/SKILL.md b/.gemini/skills/experts.implement/SKILL.md
new file mode 100644
index 00000000..709b8a23
--- /dev/null
+++ b/.gemini/skills/experts.implement/SKILL.md
@@ -0,0 +1,154 @@
+# :implement
+#
+# Generate step instruction files and sync skills from the validated workflow.yml
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Generate step instruction files and sync skills from the validated workflow.yml"
+
+prompt = """
+# :implement
+
+**Step /** in **** workflow
+
+>
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/:review_workflow_spec`
+
+## Instructions
+
+**Goal**: Generate step instruction files and sync skills from the validated workflow.yml
+
+# Implement Workflow Steps
+
+## Objective
+
+Generate the step instruction files for each step in the validated `workflow.yml` specification, then sync the skills.
+
+## Task
+
+Read the `workflow.yml` specification and create all necessary files to make the workflow functional, then sync the commands.
+
+### Step 1: Create Directory Structure
+
+Create the workflow directories:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/{steps,hooks}
+```
+
+This creates:
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/` - Main workflow directory
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/` - Step instruction files
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/hooks/` - Custom validation scripts
+
+**Note**: If directory exists from define step, skip or just create missing subdirectories.
+
+### Step 2: Read and Validate the Specification
+
+1. Read `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+2. Validate: name, version, summary, steps are present
+3. Check dependencies reference existing steps, no circular dependencies
+4. Verify file inputs match dependencies
+
+### Step 3: Generate Step Instruction Files
+
+For each step in workflow.yml, create `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`.
+
+**Guidelines**:
+
+1. **Use the workflow description** - It provides crucial context
+2. **Be specific** - Tailor instructions to the step's purpose, not generic
+3. **Provide examples** - Show what good output looks like
+4. **Explain the "why"** - Help understand the step's role in the workflow
+5. **Ask structured questions** - Steps with user inputs MUST explicitly tell the agent to "ask structured questions"
+6. **Align with hooks** - If step has hooks, ensure quality criteria match
+
+Each instruction file should include:
+- **Objective** - What this step accomplishes
+- **Task** - Detailed process
+- **Output Format** - Examples of expected outputs
+- **Quality Criteria** - How to verify completion
+
+### Step 4: Verify workflow.yml Location
+
+Ensure `workflow.yml` is at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+### Step 5: Sync Skills
+
+Run:
+
+```bash
+deepwork sync
+```
+
+This generates skills in `.claude/skills/` (or appropriate platform directory).
+
+### Step 6: Consider Rules
+
+After implementing, consider whether **rules** would help this workflow's domain.
+
+**What are rules?** Automated guardrails that trigger when certain files change, ensuring:
+- Documentation stays in sync
+- Team guidelines are followed
+- Quality standards are maintained
+
+**When to suggest rules:**
+- Does this workflow produce outputs that other files depend on?
+- Are there docs that should update when outputs change?
+- Could changes impact other parts of the project?
+
+**Examples**:
+| Workflow Type | Potential Rule |
+|---------------|----------------|
+| API Design | "Update API docs when endpoint definitions change" |
+| Competitive Research | "Update strategy docs when competitor analysis changes" |
+| Feature Development | "Update changelog when feature files change" |
+
+If you identify helpful rules, explain what they would do and offer: "Would you like me to create this rule? I can run `/deepwork-rules.define` to set it up."
+
+**Note**: Not every workflow needs rules. Only suggest when genuinely helpful.
+
+## Completion Checklist
+
+- [ ] workflow.yml in correct location
+- [ ] All step instruction files created (not stubs)
+- [ ] Instructions are specific and actionable
+- [ ] Output examples provided
+- [ ] Quality criteria defined for each step
+- [ ] `deepwork sync` executed successfully
+- [ ] Considered relevant rules for this workflow
+
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml` (from `review_workflow_spec`)
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/` (directory)
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/"
+3. **Workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/implement.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.improve_and_rereview/SKILL.md b/.gemini/skills/experts.improve_and_rereview/SKILL.md
new file mode 100644
index 00000000..517be33d
--- /dev/null
+++ b/.gemini/skills/experts.improve_and_rereview/SKILL.md
@@ -0,0 +1,282 @@
+# :improve_and_rereview
+#
+# Apply expert feedback and re-run reviews until no further feedback or 3 iterations
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Apply expert feedback and re-run reviews until no further feedback or 3 iterations"
+
+prompt = """
+# :improve_and_rereview
+
+**Step /** in **** workflow
+
+>
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/:deep_review`
+
+## Instructions
+
+**Goal**: Apply expert feedback and re-run reviews until no further feedback or 3 iterations
+
+# Improve and Re-Review
+
+## Objective
+
+Iteratively improve the PR based on expert feedback, then re-run expert reviews until all feedback is addressed or a maximum of 3 iterations is reached.
+
+## Task
+
+Apply improvements based on expert review feedback, get user approval, then have the same experts re-review. Repeat until experts report no further issues.
+
+### Process
+
+1. **Read expert reviews**
+
+ Read all review files from `pr_review/[expert_name]/review.md` to understand:
+ - What issues each expert identified within their domain
+ - What code changes they suggested
+ - Their approval status
+
+2. **Consolidate and prioritize feedback**
+
+ Group feedback by:
+ - **Critical/Major issues**: Must be addressed
+ - **Minor issues**: Should be addressed
+ - **Suggestions**: Nice to have
+
+ Present a summary to the user:
+ ```
+ ## Feedback Summary
+
+ ### Critical/Major Issues (must fix)
+ 1. [Issue from expert X]: [description]
+ 2. [Issue from expert Y]: [description]
+
+ ### Minor Issues (should fix)
+ 1. [Issue]: [description]
+
+ ### Suggestions (optional)
+ 1. [Suggestion]: [description]
+
+ ## Proposed Changes
+
+ I recommend making these changes:
+ 1. [Change 1]: [what and why]
+ 2. [Change 2]: [what and why]
+
+ Do you approve these changes?
+ ```
+
+3. **Get user approval**
+
+ **IMPORTANT**: Do not make changes without explicit user approval.
+
+ Wait for user to:
+ - Approve the proposed changes
+ - Modify which changes to apply
+ - Skip certain suggestions with justification
+
+4. **Apply approved changes**
+
+ Make the code changes the user approved:
+ - Use the Edit tool to modify files
+ - Follow the expert's code suggestions where applicable
+ - Ensure changes are coherent and don't break other code
+
+5. **Re-run expert reviews**
+
+ After applying changes, invoke the same relevant experts again:
+ - Use the Task tool with `subagent_type`: "experts"
+ - Provide the updated file contents and diff
+ - Ask them to re-review from their domain perspective
+
+ **Expert re-review prompt**:
+ ```
+ This is a re-review of PR changes after addressing your previous feedback.
+
+ Your domain: [brief description from discovery_description]
+
+ Previous issues you raised within your domain:
+ [list their issues from last review]
+
+ Changes made:
+ [summary of changes applied]
+
+ Updated files:
+ [full file contents]
+
+ From your expert perspective, please review and report:
+ 1. Are your previous domain-specific issues addressed?
+ 2. Any new issues introduced within your domain?
+ 3. Updated approval status for your domain
+ ```
+
+6. **Evaluate results**
+
+ After re-review:
+ - If ALL experts now approve (or only have minor suggestions): **Stop - review complete**
+ - If any expert still has Critical/Major issues: **Continue to next iteration**
+ - If this was iteration 3: **Stop - maximum iterations reached**
+
+7. **Create iteration summary**
+
+ Document what happened in this iteration.
+
+### Iteration Loop
+
+Repeat steps 2-7 until:
+- All experts report no further blocking feedback, OR
+- 3 iterations have been completed
+
+## Output Format
+
+### pr_review/iteration_[n]/summary.md
+
+Create one file per iteration:
+
+```markdown
+# Iteration [n] Summary
+
+**Date**: [YYYY-MM-DD]
+**Iteration**: [n] of 3 max
+
+## Feedback Addressed
+
+**IMPORTANT**: List ALL issues from expert reviews - none may be silently omitted.
+
+### From [expert-name]
+| Issue | Severity | Status | Resolution |
+|-------|----------|--------|------------|
+| [Issue 1] | Major | FIXED | [How it was fixed] |
+| [Issue 2] | Minor | SKIPPED | [Explicit reason: false positive / deferred / user declined] |
+| [Issue 3] | Suggestion | FIXED | [How it was fixed] |
+
+### From [expert-name-2]
+...
+
+## Changes Made
+
+1. **[file.py]**: [What was changed]
+2. **[other.ts]**: [What was changed]
+
+## Re-Review Results
+
+| Expert | Previous Status | New Status | Remaining Issues |
+|--------|-----------------|------------|------------------|
+| [name] | CHANGES_REQUESTED | APPROVED | 0 |
+| [name] | CHANGES_REQUESTED | COMMENTS | 1 minor |
+
+## Outcome
+
+[One of:]
+- **COMPLETE**: All experts approved. PR ready to merge.
+- **CONTINUING**: [n] blocking issues remain. Proceeding to iteration [n+1].
+- **MAX_ITERATIONS**: Reached 3 iterations. [n] issues remain unresolved.
+
+## Remaining Issues (if any)
+
+1. [Issue]: [Why not addressed / Plan for addressing]
+```
+
+### Final Summary (after last iteration)
+
+Update or create `pr_review/final_summary.md`:
+
+```markdown
+# PR Review Final Summary
+
+**PR**: #[number]
+**Total Iterations**: [n]
+**Final Status**: [APPROVED / PARTIAL / UNRESOLVED]
+
+## Review Timeline
+
+| Iteration | Date | Issues Addressed | Remaining |
+|-----------|------|------------------|-----------|
+| 1 | [date] | 5 | 3 |
+| 2 | [date] | 3 | 0 |
+
+## Expert Final Status
+
+| Expert | Final Status |
+|--------|--------------|
+| [name] | APPROVED |
+| [name] | APPROVED |
+
+## Key Improvements Made
+
+1. [Improvement 1]
+2. [Improvement 2]
+
+## Issue Resolution Summary
+
+| Expert | Total Issues | Fixed | Skipped (with reason) |
+|--------|--------------|-------|----------------------|
+| [name] | 4 | 3 | 1 (false positive) |
+| [name] | 2 | 2 | 0 |
+
+## Unresolved Items (if any)
+
+For each unresolved item, document:
+- Issue description
+- Why it wasn't fixed
+- Plan for addressing (if applicable)
+
+## Next Steps
+
+[What to do with the PR now]
+```
+
+## Quality Criteria
+
+- **Complete issue accounting**: Every issue from expert reviews is either:
+ - Fixed (with description of the fix), OR
+ - Explicitly skipped with documented justification (e.g., "false positive", "deferred to future PR", "user declined")
+- **No silent omissions**: The feedback summary must list ALL issues from expert reviews, not a subset
+- User approved suggested improvements before they were applied
+- Re-reviews were run after each improvement iteration
+- Cycle stopped when all experts reported no further feedback OR after 3 iterations
+- Final iteration summary documents the outcome for every issue
+- All changes are tracked and attributed to expert feedback
+
+## Context
+
+This is the final phase of the expert-driven PR review workflow. The iterative approach ensures feedback is actually addressed, not just acknowledged. The 3-iteration limit prevents infinite loops while allowing reasonable time for improvements.
+
+User approval at each step keeps humans in control - experts suggest, but humans decide what changes to make. This respects developer autonomy while benefiting from expert review.
+
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `pr_review/{expert_name}/review.md` (from `deep_review`)
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/iteration_{n}/summary.md`
+- `pr_review/final_summary.md`
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: pr_review/iteration_{n}/summary.md, pr_review/final_summary.md"
+3. **Workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/improve_and_rereview.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.learn/SKILL.md b/.gemini/skills/experts.learn/SKILL.md
new file mode 100644
index 00000000..d68c5a47
--- /dev/null
+++ b/.gemini/skills/experts.learn/SKILL.md
@@ -0,0 +1,165 @@
+# :learn
+#
+# Analyze conversation to extract learnings and improve instructions
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Analyze conversation to extract learnings and improve instructions"
+
+prompt = """
+# :learn
+
+**Step /** in **** workflow
+
+>
+
+
+## Instructions
+
+**Goal**: Analyze conversation to extract learnings and improve instructions
+
+# Learn from Workflow Execution
+
+## Objective
+
+Reflect on the conversation to identify learnings from DeepWork workflow executions, improve workflow instructions with generalizable insights, and capture run-specific learnings in AGENTS.md files.
+
+## Task
+
+Analyze conversation history to extract learnings, then apply them:
+- **Generalizable learnings** -> Update workflow instruction files
+- **Bespoke learnings** (run-specific) -> Add to AGENTS.md in the working folder
+
+### Step 1: Analyze Conversation for Workflow Executions
+
+1. **Scan the conversation** for DeepWork slash commands (`/expert-name.step_id`)
+2. **Identify the target folder** - the deepest common folder for all work on the topic
+3. **If no workflow specified**, ask which workflow to learn from
+
+### Step 2: Identify Confusion and Inefficiency
+
+Review the conversation for:
+
+**Confusion signals**:
+- Unnecessary questions the agent asked
+- Misunderstandings about step requirements
+- Incorrect outputs needing correction
+- Ambiguous instructions causing wrong interpretations
+
+**Inefficiency signals**:
+- Extra iterations needed
+- Repeated information
+- Missing context
+- Unclear dependencies
+
+**Error patterns**:
+- Failed validations and why
+- Misunderstood quality criteria
+- Unhandled edge cases
+
+**Success patterns**:
+- What worked well
+- Efficient approaches worth preserving
+- Good examples to add to instructions
+
+### Step 3: Classify Learnings
+
+For each learning, determine if it is:
+
+**Generalizable** (update instructions):
+- Would help ANY future run of this workflow
+- Addresses unclear or missing guidance
+- Fixes incorrect assumptions
+- Adds helpful examples
+
+**Doc spec-related** (update doc spec files):
+- Improvements to document quality criteria
+- Changes to document structure or format
+
+**Bespoke** (add to AGENTS.md):
+- Specific to THIS project/codebase/run
+- Depends on local conventions
+- References specific files or paths
+- Would not apply to other uses
+
+### Step 4: Update Workflow Instructions (Generalizable)
+
+For each generalizable learning:
+
+1. Locate `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`
+2. Make targeted improvements:
+ - Add missing context
+ - Include helpful examples
+ - Clarify ambiguous instructions
+ - Update quality criteria
+3. Keep instructions concise - avoid redundancy
+4. Preserve structure (Objective, Task, Output Format, Quality Criteria)
+
+### Step 5: Update Doc Specs (if applicable)
+
+If doc spec-related learnings identified:
+
+1. Locate doc spec at `.deepwork/doc_specs/[doc_spec_name].md`
+2. Update quality_criteria, example document, or metadata as needed
+
+### Step 6: Create/Update AGENTS.md (Bespoke)
+
+1. Place in the deepest common folder for the topic
+2. Use file references instead of duplicating content: "See `path/to/file.ext` for [description]"
+
+**Good patterns** (references):
+```markdown
+- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
+- Configuration schema: Defined in `config/schema.json`
+```
+
+**Avoid** (duplicating):
+```markdown
+- API endpoints should return JSON with this format: { status: ..., data: ... }
+```
+
+### Step 7: Update Workflow Version and Sync
+
+If instructions were modified:
+
+1. Bump version in workflow.yml (patch for improvements, minor for quality criteria changes)
+2. Add changelog entry
+3. Run `deepwork sync`
+
+## Output
+
+- Updated workflow instructions (generalizable learnings)
+- Updated doc specs (if applicable)
+- AGENTS.md with bespoke learnings in the correct working folder
+
+
+
+## Required Inputs
+
+**User Parameters** - Gather from user before starting:
+- **expert_name**: Name of the expert whose workflow to learn from (optional - will scan conversation if not provided)
+
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/` (directory)
+- `AGENTS.md`
+
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/, AGENTS.md"
+3. **Workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/learn.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.new_workflow/SKILL.md b/.gemini/skills/experts.new_workflow/SKILL.md
new file mode 100644
index 00000000..52d44332
--- /dev/null
+++ b/.gemini/skills/experts.new_workflow/SKILL.md
@@ -0,0 +1,55 @@
+#
+#
+#
+#
+# Generated by DeepWork - do not edit manually
+
+description = ""
+
+prompt = """
+#
+
+**Multi-step workflow**:
+
+> **NOTE**: Gemini CLI requires manual command invocation. After each step, tell the user which command to run next.
+
+
+## Available Steps
+
+1. **define** - Create a workflow.yml by gathering requirements through structured questions
+ Command: `/experts.define`
+2. **review_workflow_spec** - Review workflow.yml against quality criteria using a sub-agent for unbiased validation (requires: define)
+ Command: `/experts.review_workflow_spec`
+3. **implement** - Generate step instruction files and sync skills from the validated workflow.yml (requires: review_workflow_spec)
+ Command: `/experts.implement`
+
+## Execution Instructions
+
+### Step 1: Analyze Intent
+
+Parse any text following `/` to determine user intent:
+- "define" or related terms → start at `/experts.define`
+- "review_workflow_spec" or related terms → start at `/experts.review_workflow_spec`
+- "implement" or related terms → start at `/experts.implement`
+
+### Step 2: Direct User to Starting Step
+
+Tell the user which command to run:
+```
+/experts.define
+```
+
+### Step 3: Guide Through Workflow
+
+After each step completes, tell the user the next command to run until workflow is complete.
+
+### Handling Ambiguous Intent
+
+If user intent is unclear:
+- Present available steps as numbered options
+- Ask user to select the starting point
+
+## Reference
+
+- Job definition: `.deepwork/jobs//job.yml`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.review_pr/SKILL.md b/.gemini/skills/experts.review_pr/SKILL.md
new file mode 100644
index 00000000..cc7a70f1
--- /dev/null
+++ b/.gemini/skills/experts.review_pr/SKILL.md
@@ -0,0 +1,55 @@
+#
+#
+#
+#
+# Generated by DeepWork - do not edit manually
+
+description = ""
+
+prompt = """
+#
+
+**Multi-step workflow**:
+
+> **NOTE**: Gemini CLI requires manual command invocation. After each step, tell the user which command to run next.
+
+
+## Available Steps
+
+1. **check_relevance** - Invoke all experts in parallel to assess if PR changes are relevant to their domain
+ Command: `/experts.check_relevance`
+2. **deep_review** - Invoke only relevant experts to perform detailed code review (requires: check_relevance)
+ Command: `/experts.deep_review`
+3. **improve_and_rereview** - Apply expert feedback and re-run reviews until no further feedback or 3 iterations (requires: deep_review)
+ Command: `/experts.improve_and_rereview`
+
+## Execution Instructions
+
+### Step 1: Analyze Intent
+
+Parse any text following `/` to determine user intent:
+- "check_relevance" or related terms → start at `/experts.check_relevance`
+- "deep_review" or related terms → start at `/experts.deep_review`
+- "improve_and_rereview" or related terms → start at `/experts.improve_and_rereview`
+
+### Step 2: Direct User to Starting Step
+
+Tell the user which command to run:
+```
+/experts.check_relevance
+```
+
+### Step 3: Guide Through Workflow
+
+After each step completes, tell the user the next command to run until workflow is complete.
+
+### Handling Ambiguous Intent
+
+If user intent is unclear:
+- Present available steps as numbered options
+- Ask user to select the starting point
+
+## Reference
+
+- Job definition: `.deepwork/jobs//job.yml`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/experts.review_workflow_spec/SKILL.md b/.gemini/skills/experts.review_workflow_spec/SKILL.md
new file mode 100644
index 00000000..8c9c3733
--- /dev/null
+++ b/.gemini/skills/experts.review_workflow_spec/SKILL.md
@@ -0,0 +1,141 @@
+# :review_workflow_spec
+#
+# Review workflow.yml against quality criteria using a sub-agent for unbiased validation
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Review workflow.yml against quality criteria using a sub-agent for unbiased validation"
+
+prompt = """
+# :review_workflow_spec
+
+**Step /** in **** workflow
+
+>
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/:define`
+
+## Instructions
+
+**Goal**: Review workflow.yml against quality criteria using a sub-agent for unbiased validation
+
+# Review Workflow Specification
+
+## Objective
+
+Review the `workflow.yml` created in the define step against quality criteria, then iterate on fixes until all criteria pass.
+
+## Why This Step Exists
+
+The define step focuses on understanding user requirements. This review step ensures the specification meets quality standards before implementation. A fresh review catches issues that might be missed after being deeply involved in definition.
+
+## Task
+
+Review the workflow.yml against all quality criteria, fix any failures, and repeat until all pass.
+
+### Step 1: Read the Files
+
+Read the workflow specification:
+```
+.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml
+```
+
+### Step 2: Evaluate Against Quality Criteria
+
+Review the workflow.yml against these criteria:
+
+1. **Valid Identifier** - Workflow name is lowercase with underscores, matches folder name
+2. **Semantic Version** - Version follows X.Y.Z format (e.g., `1.0.0`)
+3. **Concise Summary** - Summary is under 200 characters and clearly describes the workflow
+4. **Rich Description** - Description is multi-line and explains: problem solved, process, expected outcomes, target users
+5. **Complete Steps** - Each step has: id, name, description, instructions_file, outputs
+6. **Unique Step IDs** - Step IDs are unique within the expert (across all workflows)
+7. **Valid Dependencies** - Dependencies reference existing step IDs with no circular references
+8. **Input Consistency** - File inputs with `from_step` reference a step in the dependencies array
+9. **Output Paths** - Outputs are valid filenames or paths
+
+For each criterion, determine PASS or FAIL. If FAIL, note the specific issue and fix.
+
+### Step 3: Fix Failed Criteria
+
+For each failed criterion, edit the workflow.yml:
+
+| Criterion | Common Issue | Fix |
+|-----------|-------------|-----|
+| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
+| Semantic Version | Invalid format | Set to `"1.0.0"` |
+| Concise Summary | Too long | Shorten to <200 chars |
+| Rich Description | Single line | Add multi-line explanation |
+| Complete Steps | Missing fields | Add required fields |
+| Unique Step IDs | Duplicate ID | Rename to unique identifier |
+| Valid Dependencies | Non-existent step | Fix step ID reference |
+| Input Consistency | from_step not in deps | Add step to dependencies |
+| Output Paths | Invalid format | Use valid filename/path |
+
+### Step 4: Re-Evaluate (If Needed)
+
+If any criteria failed:
+1. Review the updated workflow.yml
+2. Re-evaluate all criteria
+3. Fix remaining issues
+4. Repeat until all pass
+
+### Step 5: Confirm Completion
+
+When all criteria pass:
+
+1. Announce: "All workflow spec quality criteria pass."
+2. List what was validated
+3. Guide to next step: "Run `/experts.implement` to generate the step instruction files."
+
+## Output
+
+The validated `workflow.yml` file at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml` that passes all quality criteria.
+
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml` (from `define`)
+
+## Work Branch
+
+Use branch format: `deepwork/-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `.deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml`
+
+## Quality Validation (Manual)
+
+**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
+
+**Criteria (all must be satisfied)**:
+1. Workflow name is lowercase with underscores, no spaces or special characters
+2. Version follows X.Y.Z format (e.g., 1.0.0)
+3. Summary is under 200 characters and clearly describes the workflow
+4. Description is multi-line and explains problem, process, outcomes, and users
+5. Each step has id, name, description, instructions_file, outputs
+6. Step IDs are unique within the expert (across all workflows)
+7. Dependencies reference existing step IDs with no circular references
+8. File inputs with from_step reference a step in the dependencies array
+9. Outputs are valid filenames or paths
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step / complete, outputs: .deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+3. **Tell user next command**: `/:implement`
+
+---
+
+**Reference files**: `.deepwork/jobs//job.yml`, `.deepwork/jobs//steps/review_workflow_spec.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/review_pr/check_relevance.toml b/.gemini/skills/review_pr/check_relevance.toml
new file mode 100644
index 00000000..88ba99f3
--- /dev/null
+++ b/.gemini/skills/review_pr/check_relevance.toml
@@ -0,0 +1,204 @@
+# review_pr:check_relevance
+#
+# Invokes all available experts in parallel to assess if PR changes are relevant to their domain. Use at the start of a PR review.
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Invokes all available experts in parallel to assess if PR changes are relevant to their domain. Use at the start of a PR review."
+
+prompt = """
+# review_pr:check_relevance
+
+**Step 1/3** in **review_pr** workflow
+
+> Coordinates expert-driven PR review with iterative improvement cycles until all feedback is addressed.
+
+
+## Instructions
+
+**Goal**: Invokes all available experts in parallel to assess if PR changes are relevant to their domain. Use at the start of a PR review.
+
+# Check Expert Relevance
+
+## Objective
+
+Invoke all available experts in parallel to determine which ones have relevant expertise for the PR changes. This filtering step ensures only applicable experts spend time on detailed review.
+
+## Task
+
+Assess which domain experts can meaningfully contribute to reviewing the current PR by having each expert examine the changes and report their relevance.
+
+### Process
+
+1. **Get PR information**
+
+ Determine the PR to review:
+ ```bash
+ # If pr_number input provided, use it
+ # Otherwise, get PR for current branch
+ gh pr view --json number,title,headRefName
+ ```
+
+ Get the changed files:
+ ```bash
+ gh pr diff --name-only
+ ```
+
+ Get the actual diff for context:
+ ```bash
+ gh pr diff
+ ```
+
+2. **Discover available experts**
+
+ List all experts in the project:
+ ```bash
+ ls -1 .deepwork/experts/
+ ```
+
+ Read each expert's `discovery_description` from their `expert.yml` to understand their domain.
+
+3. **Invoke experts in parallel**
+
+ For each expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "expert"
+ - `agent`: `[expert-name]` (the folder name with underscores as dashes)
+ - `prompt`: Provide the expert with:
+ - The list of changed files
+ - A summary of the diff (or key portions)
+ - Ask them to determine if changes fall within their domain of expertise
+
+ **Expert prompt template**:
+ ```
+ Review this PR to determine if the changes are relevant to your domain of expertise.
+
+ Changed files:
+ [list of files]
+
+ Key changes:
+ [summary or excerpts from diff]
+
+ Respond with:
+ 1. RELEVANT or NOT_RELEVANT
+ 2. Brief justification (1-2 sentences)
+ 3. If relevant, which specific files/changes you can review
+ ```
+
+ **Important**: Invoke ALL experts in parallel to minimize latency.
+
+4. **Collect and summarize responses**
+
+ Wait for all expert responses. Compile into the output file.
+
+## Output Format
+
+### pr_review/relevance_assessments.md
+
+Create this file with the assessment results:
+
+```markdown
+# PR Relevance Assessment
+
+**PR**: #[number] - [title]
+**Branch**: [branch_name]
+**Date**: [YYYY-MM-DD]
+
+## Changed Files
+
+- path/to/file1.py
+- path/to/file2.ts
+- ...
+
+## Expert Assessments
+
+### [expert-name-1]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+### [expert-name-2]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+...
+
+## Summary
+
+**Relevant experts**: [list of expert names marked RELEVANT]
+**Next step**: Run `/review_pr.deep_review` with these experts
+```
+
+## Quality Criteria
+
+- All experts in `.deepwork/experts/` were invoked in parallel
+- Each expert provided a yes/no relevance determination with justification
+- Relevant experts are clearly identified for the next step
+- PR metadata (number, title, branch) is captured
+- Changed files are listed for reference
+- When all criteria are met, include `Quality Criteria Met` in your response
+
+## Context
+
+This is the first phase of the expert-driven PR review workflow. By checking relevance first, we avoid wasting expert review time on changes outside their domain. The parallel invocation ensures this filtering step completes quickly even with many experts.
+
+The output file serves as input to the `deep_review` step, which will only invoke experts marked as RELEVANT.
+
+
+### Job Context
+
+A multi-phase workflow for comprehensive pull request review using domain experts.
+
+The workflow operates in three phases:
+1. **Relevance Check**: All available experts examine the PR changes in parallel to determine
+ if any changes fall within their domain of expertise.
+2. **Deep Review**: Experts who found relevant changes perform detailed code review, producing
+ written feedback and specific code change suggestions.
+3. **Improvement Cycle**: The PR is improved based on feedback, then re-reviewed by the same
+ experts. This cycle repeats until all experts report no further feedback or 3 iterations
+ are reached.
+
+Produces:
+- Relevance assessments from all experts
+- Detailed review feedback with code suggestions
+- Iteratively improved PR code
+
+Requires: PR branch checked out, `gh` CLI available for PR metadata
+
+
+## Required Inputs
+
+**User Parameters** - Gather from user before starting:
+- **pr_number**: Optional PR number (defaults to current branch's PR)
+
+
+## Work Branch
+
+Use branch format: `deepwork/review_pr-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/review_pr-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/relevance_assessments.md`
+
+## Quality Validation (Manual)
+
+**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
+
+**Criteria (all must be satisfied)**:
+1. All experts in .deepwork/experts/ were invoked in parallel
+2. Each expert provided a yes/no relevance determination with justification
+3. Relevant experts are clearly identified for the next step
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step 1/3 complete, outputs: pr_review/relevance_assessments.md"
+3. **Tell user next command**: `/review_pr:deep_review`
+
+---
+
+**Reference files**: `.deepwork/jobs/review_pr/job.yml`, `.deepwork/jobs/review_pr/steps/check_relevance.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/review_pr/deep_review.toml b/.gemini/skills/review_pr/deep_review.toml
new file mode 100644
index 00000000..48f66c9e
--- /dev/null
+++ b/.gemini/skills/review_pr/deep_review.toml
@@ -0,0 +1,265 @@
+# review_pr:deep_review
+#
+# Invokes only relevant experts (from previous step) to perform detailed code review. Use after relevance check identifies applicable experts.
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Invokes only relevant experts (from previous step) to perform detailed code review. Use after relevance check identifies applicable experts."
+
+prompt = """
+# review_pr:deep_review
+
+**Step 2/3** in **review_pr** workflow
+
+> Coordinates expert-driven PR review with iterative improvement cycles until all feedback is addressed.
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/review_pr:check_relevance`
+
+## Instructions
+
+**Goal**: Invokes only relevant experts (from previous step) to perform detailed code review. Use after relevance check identifies applicable experts.
+
+# Deep Expert Review
+
+## Objective
+
+Have relevant domain experts perform thorough code review of the PR changes within their expertise. Each expert produces detailed written feedback with specific suggestions for improvement.
+
+## Task
+
+Invoke only the experts marked as RELEVANT in the previous step to perform detailed code review, producing actionable feedback.
+
+### Process
+
+1. **Read the relevance assessments**
+
+ Read `pr_review/relevance_assessments.md` to identify:
+ - Which experts were marked RELEVANT
+ - Which files each expert should focus on
+ - The PR number and branch
+
+2. **Get the full PR diff**
+
+ ```bash
+ gh pr diff
+ ```
+
+ Also read the actual file contents for files each expert will review.
+
+3. **Invoke relevant experts for deep review**
+
+ For each RELEVANT expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "expert"
+ - `agent`: `[expert-name]`
+ - `prompt`: Provide the expert with:
+ - The full content of files in their domain
+ - The diff for those files
+ - Instructions to perform thorough code review
+
+ **Expert prompt template**:
+ ```
+ Perform a detailed code review of these PR changes within your domain of expertise.
+
+ Files to review:
+ [full file contents]
+
+ Changes (diff):
+ [relevant portion of diff]
+
+ Provide your review in this format:
+
+ ## Summary
+ Overall assessment of the changes (1-2 paragraphs)
+
+ ## Issues Found
+ For each issue:
+ - **File**: path/to/file
+ - **Line(s)**: [line numbers]
+ - **Severity**: Critical / Major / Minor / Suggestion
+ - **Issue**: Description of the problem
+ - **Suggestion**: How to fix it
+
+ ## Code Suggestions
+ Specific code changes you recommend (include before/after snippets)
+
+ ## Approval Status
+ - APPROVED: No blocking issues
+ - CHANGES_REQUESTED: Blocking issues must be addressed
+ - COMMENTS: Suggestions only, no blockers
+ ```
+
+ **Note**: Invoke relevant experts in parallel for efficiency.
+
+4. **Collect expert reviews**
+
+ Wait for all expert reviews to complete. Save each to their dedicated review file.
+
+5. **Create consolidated summary**
+
+ After all reviews complete, summarize across experts:
+ - Total issues by severity
+ - Key themes across reviews
+ - Overall approval status
+
+## Output Format
+
+### pr_review/[expert_name]/review.md
+
+Create one file per relevant expert:
+
+```markdown
+# [Expert Name] Review
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+**Reviewer**: [expert-name] expert
+
+## Summary
+
+[Expert's overall assessment]
+
+## Issues Found
+
+### Issue 1
+- **File**: path/to/file.py
+- **Line(s)**: 45-52
+- **Severity**: Major
+- **Issue**: [Description]
+- **Suggestion**: [How to fix]
+
+### Issue 2
+...
+
+## Code Suggestions
+
+### Suggestion 1: [Brief title]
+
+**File**: path/to/file.py
+
+Before:
+```python
+# problematic code
+```
+
+After:
+```python
+# suggested improvement
+```
+
+**Rationale**: [Why this change improves the code]
+
+...
+
+## Approval Status
+
+[APPROVED / CHANGES_REQUESTED / COMMENTS]
+
+[Additional notes on approval status]
+```
+
+### pr_review/review_summary.md (optional but recommended)
+
+```markdown
+# PR Review Summary
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+
+## Expert Reviews
+
+| Expert | Status | Critical | Major | Minor |
+|--------|--------|----------|-------|-------|
+| [name] | CHANGES_REQUESTED | 0 | 2 | 3 |
+| [name] | APPROVED | 0 | 0 | 1 |
+
+## Key Themes
+
+- [Theme 1 appearing across multiple reviews]
+- [Theme 2]
+
+## Overall Status
+
+[CHANGES_REQUESTED if any expert requested changes, otherwise APPROVED]
+
+## Next Steps
+
+[If changes requested]: Run `/review_pr.improve_and_rereview` to address feedback
+[If approved]: PR is ready to merge
+```
+
+## Quality Criteria
+
+- Only experts marked as relevant were invoked
+- Each expert produced written feedback in their review file
+- Specific code change suggestions are included where applicable
+- Issues include file, line numbers, severity, and suggestions
+- Approval status is clearly stated by each expert
+- When all criteria are met, include `Quality Criteria Met` in your response
+
+## Context
+
+This is the second phase of the expert-driven PR review workflow. Deep review only happens after relevance filtering to ensure expert time is well-spent. Each expert reviews independently, bringing their domain knowledge to identify issues others might miss.
+
+The review files serve as input to the `improve_and_rereview` step if changes are requested. If all experts approve, the PR can proceed to merge.
+
+
+### Job Context
+
+A multi-phase workflow for comprehensive pull request review using domain experts.
+
+The workflow operates in three phases:
+1. **Relevance Check**: All available experts examine the PR changes in parallel to determine
+ if any changes fall within their domain of expertise.
+2. **Deep Review**: Experts who found relevant changes perform detailed code review, producing
+ written feedback and specific code change suggestions.
+3. **Improvement Cycle**: The PR is improved based on feedback, then re-reviewed by the same
+ experts. This cycle repeats until all experts report no further feedback or 3 iterations
+ are reached.
+
+Produces:
+- Relevance assessments from all experts
+- Detailed review feedback with code suggestions
+- Iteratively improved PR code
+
+Requires: PR branch checked out, `gh` CLI available for PR metadata
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `pr_review/relevance_assessments.md` (from `check_relevance`)
+
+## Work Branch
+
+Use branch format: `deepwork/review_pr-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/review_pr-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/[expert_name]/review.md`
+
+## Quality Validation (Manual)
+
+**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
+
+**Criteria (all must be satisfied)**:
+1. Only experts marked as relevant were invoked
+2. Each expert produced written feedback in their review file
+3. Specific code change suggestions are included where applicable
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step 2/3 complete, outputs: pr_review/[expert_name]/review.md"
+3. **Tell user next command**: `/review_pr:improve_and_rereview`
+
+---
+
+**Reference files**: `.deepwork/jobs/review_pr/job.yml`, `.deepwork/jobs/review_pr/steps/deep_review.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/review_pr/improve_and_rereview.toml b/.gemini/skills/review_pr/improve_and_rereview.toml
new file mode 100644
index 00000000..bc642f1b
--- /dev/null
+++ b/.gemini/skills/review_pr/improve_and_rereview.toml
@@ -0,0 +1,290 @@
+# review_pr:improve_and_rereview
+#
+# Applies expert feedback to improve the PR, then re-runs expert reviews. Cycles until no further feedback or 3 iterations. Use after deep review completes.
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Applies expert feedback to improve the PR, then re-runs expert reviews. Cycles until no further feedback or 3 iterations. Use after deep review completes."
+
+prompt = """
+# review_pr:improve_and_rereview
+
+**Step 3/3** in **review_pr** workflow
+
+> Coordinates expert-driven PR review with iterative improvement cycles until all feedback is addressed.
+
+## Prerequisites (Verify First)
+
+Before proceeding, confirm these steps are complete:
+- `/review_pr:deep_review`
+
+## Instructions
+
+**Goal**: Applies expert feedback to improve the PR, then re-runs expert reviews. Cycles until no further feedback or 3 iterations. Use after deep review completes.
+
+# Improve and Re-Review
+
+## Objective
+
+Iteratively improve the PR based on expert feedback, then re-run expert reviews until all feedback is addressed or a maximum of 3 iterations is reached.
+
+## Task
+
+Apply improvements based on expert review feedback, get user approval, then have the same experts re-review. Repeat until experts report no further issues.
+
+### Process
+
+1. **Read expert reviews**
+
+ Read all review files from `pr_review/[expert_name]/review.md` to understand:
+ - What issues each expert identified
+ - What code changes they suggested
+ - Their approval status
+
+2. **Consolidate and prioritize feedback**
+
+ Group feedback by:
+ - **Critical/Major issues**: Must be addressed
+ - **Minor issues**: Should be addressed
+ - **Suggestions**: Nice to have
+
+ Present a summary to the user:
+ ```
+ ## Feedback Summary
+
+ ### Critical/Major Issues (must fix)
+ 1. [Issue from expert X]: [description]
+ 2. [Issue from expert Y]: [description]
+
+ ### Minor Issues (should fix)
+ 1. [Issue]: [description]
+
+ ### Suggestions (optional)
+ 1. [Suggestion]: [description]
+
+ ## Proposed Changes
+
+ I recommend making these changes:
+ 1. [Change 1]: [what and why]
+ 2. [Change 2]: [what and why]
+
+ Do you approve these changes?
+ ```
+
+3. **Get user approval**
+
+ **IMPORTANT**: Do not make changes without explicit user approval.
+
+ Wait for user to:
+ - Approve the proposed changes
+ - Modify which changes to apply
+ - Skip certain suggestions with justification
+
+4. **Apply approved changes**
+
+ Make the code changes the user approved:
+ - Use the Edit tool to modify files
+ - Follow the expert's code suggestions where applicable
+ - Ensure changes are coherent and don't break other code
+
+5. **Re-run expert reviews**
+
+ After applying changes, invoke the same relevant experts again:
+ - Use the Task tool with `subagent_type`: "expert"
+ - Provide the updated file contents and diff
+ - Ask them to review the changes and identify any remaining issues
+
+ **Expert re-review prompt**:
+ ```
+ This is a re-review of PR changes after addressing your previous feedback.
+
+ Previous issues you raised:
+ [list their issues from last review]
+
+ Changes made:
+ [summary of changes applied]
+
+ Updated files:
+ [full file contents]
+
+ Please review and report:
+ 1. Are your previous issues addressed?
+ 2. Any new issues introduced?
+ 3. Updated approval status
+ ```
+
+6. **Evaluate results**
+
+ After re-review:
+ - If ALL experts now approve (or only have minor suggestions): **Stop - review complete**
+ - If any expert still has Critical/Major issues: **Continue to next iteration**
+ - If this was iteration 3: **Stop - maximum iterations reached**
+
+7. **Create iteration summary**
+
+ Document what happened in this iteration.
+
+### Iteration Loop
+
+Repeat steps 2-7 until:
+- All experts report no further blocking feedback, OR
+- 3 iterations have been completed
+
+## Output Format
+
+### pr_review/iteration_[n]/summary.md
+
+Create one file per iteration:
+
+```markdown
+# Iteration [n] Summary
+
+**Date**: [YYYY-MM-DD]
+**Iteration**: [n] of 3 max
+
+## Feedback Addressed
+
+### From [expert-name]
+- [Issue 1]: [How it was addressed]
+- [Issue 2]: [How it was addressed / Why it was skipped]
+
+### From [expert-name-2]
+...
+
+## Changes Made
+
+1. **[file.py]**: [What was changed]
+2. **[other.ts]**: [What was changed]
+
+## Re-Review Results
+
+| Expert | Previous Status | New Status | Remaining Issues |
+|--------|-----------------|------------|------------------|
+| [name] | CHANGES_REQUESTED | APPROVED | 0 |
+| [name] | CHANGES_REQUESTED | COMMENTS | 1 minor |
+
+## Outcome
+
+[One of:]
+- **COMPLETE**: All experts approved. PR ready to merge.
+- **CONTINUING**: [n] blocking issues remain. Proceeding to iteration [n+1].
+- **MAX_ITERATIONS**: Reached 3 iterations. [n] issues remain unresolved.
+
+## Remaining Issues (if any)
+
+1. [Issue]: [Why not addressed / Plan for addressing]
+```
+
+### Final Summary (after last iteration)
+
+Update or create `pr_review/final_summary.md`:
+
+```markdown
+# PR Review Final Summary
+
+**PR**: #[number]
+**Total Iterations**: [n]
+**Final Status**: [APPROVED / PARTIAL / UNRESOLVED]
+
+## Review Timeline
+
+| Iteration | Date | Issues Addressed | Remaining |
+|-----------|------|------------------|-----------|
+| 1 | [date] | 5 | 3 |
+| 2 | [date] | 3 | 0 |
+
+## Expert Final Status
+
+| Expert | Final Status |
+|--------|--------------|
+| [name] | APPROVED |
+| [name] | APPROVED |
+
+## Key Improvements Made
+
+1. [Improvement 1]
+2. [Improvement 2]
+
+## Unresolved Items (if any)
+
+[List any items not addressed and why]
+
+## Next Steps
+
+[What to do with the PR now]
+```
+
+## Quality Criteria
+
+- User approved suggested improvements before they were applied
+- Re-reviews were run after each improvement iteration
+- Cycle stopped when all experts reported no further feedback OR after 3 iterations
+- Final iteration summary documents the outcome
+- All changes are tracked and attributed to expert feedback
+- When all criteria are met, include `Quality Criteria Met` in your response
+
+## Context
+
+This is the final phase of the expert-driven PR review workflow. The iterative approach ensures feedback is actually addressed, not just acknowledged. The 3-iteration limit prevents infinite loops while allowing reasonable time for improvements.
+
+User approval at each step keeps humans in control - experts suggest, but humans decide what changes to make. This respects developer autonomy while benefiting from expert review.
+
+
+### Job Context
+
+A multi-phase workflow for comprehensive pull request review using domain experts.
+
+The workflow operates in three phases:
+1. **Relevance Check**: All available experts examine the PR changes in parallel to determine
+ if any changes fall within their domain of expertise.
+2. **Deep Review**: Experts who found relevant changes perform detailed code review, producing
+ written feedback and specific code change suggestions.
+3. **Improvement Cycle**: The PR is improved based on feedback, then re-reviewed by the same
+ experts. This cycle repeats until all experts report no further feedback or 3 iterations
+ are reached.
+
+Produces:
+- Relevance assessments from all experts
+- Detailed review feedback with code suggestions
+- Iteratively improved PR code
+
+Requires: PR branch checked out, `gh` CLI available for PR metadata
+
+
+## Required Inputs
+
+
+**Files from Previous Steps** - Read these first:
+- `pr_review/[expert_name]/review.md` (from `deep_review`)
+
+## Work Branch
+
+Use branch format: `deepwork/review_pr-[instance]-YYYYMMDD`
+
+- If on a matching work branch: continue using it
+- If on main/master: create new branch with `git checkout -b deepwork/review_pr-[instance]-$(date +%Y%m%d)`
+
+## Outputs
+
+**Required outputs**:
+- `pr_review/iteration_[n]/summary.md`
+
+## Quality Validation (Manual)
+
+**NOTE**: Gemini CLI does not support automated validation. Manually verify criteria before completing.
+
+**Criteria (all must be satisfied)**:
+1. User approved suggested improvements before they were applied
+2. Re-reviews were run after each improvement iteration
+3. Cycle stopped when all experts reported no further feedback OR after 3 iterations
+4. Final iteration summary documents the outcome
+## On Completion
+
+1. Verify outputs are created
+2. Inform user: "Step 3/3 complete, outputs: pr_review/iteration_[n]/summary.md"
+3. **Workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
+
+---
+
+**Reference files**: `.deepwork/jobs/review_pr/job.yml`, `.deepwork/jobs/review_pr/steps/improve_and_rereview.md`
+"""
\ No newline at end of file
diff --git a/.gemini/skills/review_pr/index.toml b/.gemini/skills/review_pr/index.toml
new file mode 100644
index 00000000..aa5a3879
--- /dev/null
+++ b/.gemini/skills/review_pr/index.toml
@@ -0,0 +1,73 @@
+# review_pr
+#
+# Coordinates expert-driven PR review with iterative improvement cycles until all feedback is addressed.
+#
+# Generated by DeepWork - do not edit manually
+
+description = "Coordinates expert-driven PR review with iterative improvement cycles until all feedback is addressed."
+
+prompt = """
+# review_pr
+
+**Multi-step workflow**: Coordinates expert-driven PR review with iterative improvement cycles until all feedback is addressed.
+
+> **NOTE**: Gemini CLI requires manual command invocation. After each step, tell the user which command to run next.
+
+A multi-phase workflow for comprehensive pull request review using domain experts.
+
+The workflow operates in three phases:
+1. **Relevance Check**: All available experts examine the PR changes in parallel to determine
+ if any changes fall within their domain of expertise.
+2. **Deep Review**: Experts who found relevant changes perform detailed code review, producing
+ written feedback and specific code change suggestions.
+3. **Improvement Cycle**: The PR is improved based on feedback, then re-reviewed by the same
+ experts. This cycle repeats until all experts report no further feedback or 3 iterations
+ are reached.
+
+Produces:
+- Relevance assessments from all experts
+- Detailed review feedback with code suggestions
+- Iteratively improved PR code
+
+Requires: PR branch checked out, `gh` CLI available for PR metadata
+
+
+## Available Steps
+
+1. **check_relevance** - Invokes all available experts in parallel to assess if PR changes are relevant to their domain. Use at the start of a PR review.
+ Command: `/review_pr:check_relevance`
+2. **deep_review** - Invokes only relevant experts (from previous step) to perform detailed code review. Use after relevance check identifies applicable experts. (requires: check_relevance)
+ Command: `/review_pr:deep_review`
+3. **improve_and_rereview** - Applies expert feedback to improve the PR, then re-runs expert reviews. Cycles until no further feedback or 3 iterations. Use after deep review completes. (requires: deep_review)
+ Command: `/review_pr:improve_and_rereview`
+
+## Execution Instructions
+
+### Step 1: Analyze Intent
+
+Parse any text following `/review_pr` to determine user intent:
+- "check_relevance" or related terms → start at `/review_pr:check_relevance`
+- "deep_review" or related terms → start at `/review_pr:deep_review`
+- "improve_and_rereview" or related terms → start at `/review_pr:improve_and_rereview`
+
+### Step 2: Direct User to Starting Step
+
+Tell the user which command to run:
+```
+/review_pr:check_relevance
+```
+
+### Step 3: Guide Through Workflow
+
+After each step completes, tell the user the next command to run until workflow is complete.
+
+### Handling Ambiguous Intent
+
+If user intent is unclear:
+- Present available steps as numbered options
+- Ask user to select the starting point
+
+## Reference
+
+- Job definition: `.deepwork/jobs/review_pr/job.yml`
+"""
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f9c4dc4..b9286ed0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,14 +8,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
-- Concurrent steps support in workflow definitions
- - Workflows can now specify nested arrays of step IDs to indicate steps that can run in parallel
- - Example: `steps: [setup, [task_a, task_b, task_c], finalize]` runs task_a/b/c concurrently
- - Single-item arrays indicate a step with multiple parallel instances (e.g., `[fetch_campaign_data]` runs for each campaign)
- - New `WorkflowStepEntry` dataclass in parser for sequential/concurrent step groups
- - Meta-skill template renders concurrent steps as "Background Task 1/2/3" with clear instructions
- - Added `get_step_entry_position_in_workflow()` and `get_concurrent_step_info()` methods to JobDefinition
- - Full backward compatibility: existing workflows with simple step arrays continue to work
+- New `deepwork-rules` expert with `define` workflow for creating file-triggered rules
+ - Includes documentation on action types (prompt and command)
+ - Example command action format with `action: command` and `command:` fields
+- Quality criteria for `improve_and_rereview` step requiring complete issue accounting
+ - Every issue must be explicitly FIXED or SKIPPED with documented justification
+ - Prevents silent omissions in PR review feedback
+
+### Changed
+- **BREAKING**: Merged jobs into experts as workflows
+ - Standalone `job.yml` files replaced by `workflow.yml` files inside expert directories
+ - Structure changed: `.deepwork/experts/[name]/workflows/[workflow]/workflow.yml`
+ - `deepwork_jobs` expert merged into `experts` expert (includes `new_workflow`, `learn`, `review_pr` workflows)
+ - `deepwork_rules` job became `deepwork-rules` expert with `define` workflow
+ - `review_pr` job moved into `experts` expert as `review_pr` workflow
+ - Skill naming now uses expert name: `/experts.define`, `/experts.review_pr`, `/deepwork-rules.define`
+- Renamed schema: `job_schema.py` → `workflow_schema.py`
+- Renamed templates: `skill-job-*.md.jinja` → `skill-workflow-*.md.jinja`
+- Updated install CLI "Next steps" message from `/deepwork-jobs.define` to `/experts.define`
+- Improved skill generation documentation explaining step skills vs workflow meta-skills
+
+### Fixed
+- Path traversal security: Added validation to prevent `..` in `script` and `prompt_file` paths
+- Fixed potential IndexError in hooks_syncer when Stop hook configuration is empty
+
+### Removed
+- Deleted standalone `src/deepwork/core/parser.py` and `src/deepwork/core/generator.py` (merged into experts modules)
+- Deleted `src/deepwork/standard_jobs/` directory (jobs now live inside experts)
+- Deleted `deepwork_jobs` expert (merged into `experts` expert)
+- Removed legacy job-related tests that are no longer applicable
+
+## [0.6.0] - 2026-01-30
+
+### Added
+- **Experts system** for auto-improving collections of domain knowledge
+ - New `expert.yml` format with `discovery_description` and `full_expertise` fields
+ - Topics: Detailed documentation in `topics/*.md` with name, keywords, and last_updated frontmatter
+ - Learnings: Experience-based insights in `learnings/*.md` with summarized_result frontmatter
+ - Experts live in `.deepwork/experts/[folder-name]/` directory
+- New CLI commands for experts:
+ - `deepwork topics --expert "name"` - Lists topics as markdown, sorted by most recently updated
+ - `deepwork learnings --expert "name"` - Lists learnings as markdown with summarized results
+- Expert agent generation during `deepwork sync`:
+ - Generates Claude agents in `.claude/agents/dwe_[expert-name].md`
+ - Agent name format: `[expert-name]`
+ - Uses dynamic command embedding (`$(deepwork topics ...)`) for always-current content
+- Standard "experts" expert shipped with DeepWork documenting the experts system itself
+ - Includes topics on design patterns and discovery descriptions
+ - Includes learning on keeping experts focused
+- Expert installation during `deepwork install`:
+ - Standard experts from `src/deepwork/standard/experts/` copied to `.deepwork/experts/`
+ - Creates `.deepwork/experts/` directory during installation
+- Comprehensive test suite for experts (99 new tests)
- Agent delegation field for job.yml steps
- New `agent` field on steps allows specifying an agent type (e.g., `agent: general-purpose`)
- When `agent` is set, generated Claude Code skills automatically include `context: fork` and `agent:` in frontmatter
@@ -244,7 +288,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Initial version.
-[Unreleased]: https://github.com/Unsupervisedcom/deepwork/compare/0.5.1...HEAD
+[Unreleased]: https://github.com/Unsupervisedcom/deepwork/compare/0.6.0...HEAD
+[0.6.0]: https://github.com/Unsupervisedcom/deepwork/releases/tag/0.6.0
[0.5.1]: https://github.com/Unsupervisedcom/deepwork/releases/tag/0.5.1
[0.5.0]: https://github.com/Unsupervisedcom/deepwork/releases/tag/0.5.0
[0.4.2]: https://github.com/anthropics/deepwork/compare/0.4.1...0.4.2
diff --git a/README.md b/README.md
index 76a659de..66c51779 100644
--- a/README.md
+++ b/README.md
@@ -199,12 +199,18 @@ your-project/
├── .deepwork/
│ ├── config.yml # Platform configuration
│ ├── rules/ # Automated rules
+│ ├── experts/ # Domain knowledge experts
+│ │ └── expert_name/
+│ │ ├── expert.yml # Expert definition
+│ │ ├── topics/ # Detailed topic documentation
+│ │ └── learnings/ # Experience-based insights
│ └── jobs/ # Job definitions
│ └── job_name/
│ ├── job.yml # Job metadata
│ └── steps/ # Step instructions
-├── .claude/ # Generated Claude skills
-│ └── skills/
+├── .claude/ # Generated Claude skills and agents
+│ ├── skills/
+│ └── agents/ # Expert agents (dwe_*.md)
└── deepwork-output/ # Job outputs (gitignored)
```
diff --git a/doc/architecture.md b/doc/architecture.md
index f4a2e094..fbbcaaad 100644
--- a/doc/architecture.md
+++ b/doc/architecture.md
@@ -63,7 +63,7 @@ deepwork/ # DeepWork tool repository
│ │ │ └── skill-job-step.md.jinja
│ │ ├── gemini/
│ │ └── copilot/
-│ ├── standard_jobs/ # Built-in job definitions
+│ ├── standard_jobs/ # Built-in job definitions (legacy location)
│ │ ├── deepwork_jobs/
│ │ │ ├── job.yml
│ │ │ ├── steps/
@@ -77,6 +77,13 @@ deepwork/ # DeepWork tool repository
│ │ ├── global_hooks.yml
│ │ ├── user_prompt_submit.sh
│ │ └── capture_prompt_work_tree.sh
+│ ├── standard/ # Standard assets (new consolidated location)
+│ │ └── experts/ # Built-in expert definitions
+│ │ └── experts/ # Meta-expert for the experts system itself
+│ │ ├── expert.yml
+│ │ ├── topics/
+│ │ └── learnings/
+│ │ └── .gitkeep
│ ├── schemas/ # Definition schemas
│ │ ├── job_schema.py
│ │ ├── doc_spec_schema.py # Doc spec schema definition
@@ -1344,6 +1351,20 @@ Claude: Created rule "API documentation update" in .deepwork/rules/api-documenta
---
+## Experts
+
+Experts are auto-improving collections of domain knowledge. They provide a structured mechanism for accumulating expertise and exposing it through Claude agents.
+
+See `doc/experts_requirements.md` for the full specification.
+
+**Key Concepts**:
+- Experts live in `.deepwork/experts/[name]/` with `expert.yml`, `topics/`, and `learnings/`
+- Standard experts ship from `src/deepwork/standard/experts/` and are installed during `deepwork install`
+- `deepwork sync` will generate Claude agents in `.claude/agents/` with `dwe_` prefix
+- CLI commands: `deepwork topics --expert "name"` and `deepwork learnings --expert "name"`
+
+---
+
## Technical Decisions
### Language: Python 3.11+
diff --git a/doc/experts_requirements.md b/doc/experts_requirements.md
new file mode 100644
index 00000000..c6037590
--- /dev/null
+++ b/doc/experts_requirements.md
@@ -0,0 +1,165 @@
+# Experts System
+
+> **Status**: Implemented. See `doc/architecture.md` for integration details.
+
+Experts are auto-improving collections of domain knowledge. They provide a structured mechanism for accumulating expertise and exposing it throughout the system.
+
+## Overview
+
+Each expert represents deep knowledge in a specific domain (e.g., "Ruby on Rails ActiveJob", "Social Marketing"). Experts consist of:
+- **Core expertise**: The foundational knowledge about the domain
+- **Topics**: Detailed documentation on specific subjects within the domain
+- **Learnings**: Hard-fought insights from real experiences
+
+## Directory Structure
+
+Experts live in `.deepwork/experts/[expert-folder-name]/`:
+
+```
+.deepwork/experts/
+└── rails_activejob/
+ ├── expert.yml # Core expert definition
+ ├── topics/ # Detailed topic documentation
+ │ └── retry_handling.md
+ └── learnings/ # Experience-based insights
+ └── job_errors_not_going_to_sentry.md
+```
+
+### Naming Convention
+
+The **expert name** is derived from the folder name:
+- Spaces and underscores become dashes
+- Example: folder `rails_activejob` → expert name `rails-activejob`
+- This name is used in CLI commands: `deepwork topics --expert "rails-activejob"`
+
+## File Formats
+
+### expert.yml
+
+```yaml
+discovery_description: |
+ Short description used by other parts of the system to decide
+ whether to invoke this expert. Keep it concise and specific.
+
+full_expertise: |
+ Unlimited text (but generally ~5 pages max) containing the
+ complete current knowledge of this domain. This is the core
+ expertise that gets included in the generated agent.
+```
+
+**Note**: The expert name is not a field in this file—it's derived from the folder name.
+
+### topics/*.md
+
+Topics are frontmatter Markdown files covering specific subjects within the domain.
+
+```markdown
+---
+name: Retry Handling
+keywords:
+ - retry
+ - exponential backoff
+ - dead letter queue
+last_updated: 2025-01-15
+---
+
+Detailed documentation about retry handling in ActiveJob...
+```
+
+| Field | Description |
+|-------|-------------|
+| `name` | Human-readable name (e.g., "Retry Handling") |
+| `keywords` | Topic-specific keywords only—avoid broad terms like "Rails" |
+| `last_updated` | Date stamp (manually maintained) |
+
+Filenames are purely organizational and don't affect functionality.
+
+### learnings/*.md
+
+Learnings document complex experiences and hard-fought insights—like mini retrospectives.
+
+```markdown
+---
+name: Job errors not going to Sentry
+last_updated: 2025-01-20
+summarized_result: |
+ Sentry changed their standard gem for hooking into jobs.
+ SolidQueue still worked but ActiveJobKubernetes did not.
+---
+
+## Context
+We noticed errors from background jobs weren't appearing in Sentry...
+
+## Investigation
+After debugging, we discovered that Sentry's latest gem update...
+
+## Resolution
+Updated the initialization to explicitly configure the hook...
+```
+
+| Field | Description |
+|-------|-------------|
+| `name` | Human-readable title of the learning |
+| `last_updated` | Date stamp (manually maintained) |
+| `summarized_result` | Brief summary of the key finding |
+
+## CLI Commands
+
+### List Topics
+
+```bash
+deepwork topics --expert "rails-activejob"
+```
+
+Returns a Markdown list of topics:
+- Name and relative file path as a Markdown link
+- Followed by keywords
+- Sorted by most-recently-updated
+
+### List Learnings
+
+```bash
+deepwork learnings --expert "rails-activejob"
+```
+
+Returns a Markdown list of learnings:
+- Name and relative file path as a Markdown link
+- Followed by the summarized result
+- Sorted by most-recently-updated
+
+## Sync Behavior
+
+`deepwork sync` generates Claude agents from experts (in addition to syncing jobs).
+
+### Generated Agent Location
+
+Agents are created in `.claude/agents/` with:
+- **Filename**: `dwe_[expert-name].md` (e.g., `dwe_rails-activejob.md`)
+- **name field**: `[expert-name]` (e.g., `rails-activejob`)
+- **description**: The `discovery_description` from expert.yml
+
+### Agent Body Content
+
+The agent body combines:
+1. The `full_expertise` text
+2. A topics list using Claude's dynamic command embedding:
+ ```
+ $(deepwork topics --expert "rails-activejob")
+ ```
+3. A learnings list using the same mechanism:
+ ```
+ $(deepwork learnings --expert "rails-activejob")
+ ```
+
+This ensures the agent always has access to the latest topics and learnings at runtime.
+
+## Standard Experts
+
+Standard experts ship with DeepWork and are located at `src/deepwork/standard/experts/`.
+
+When `deepwork install` runs, these are copied into `.deepwork/experts/` in the target project.
+
+## Future Considerations
+
+- Experts and jobs are currently independent systems
+- Future versions may enable experts to reference each other and collaborate with jobs
diff --git a/flake.nix b/flake.nix
index d2218afb..b39b2ed3 100644
--- a/flake.nix
+++ b/flake.nix
@@ -68,9 +68,9 @@
uv venv .venv --quiet
fi
- # Sync dependencies (including dev extras like pytest, ruff, mypy)
+ # Sync dependencies (including dev group: pytest, ruff, mypy)
# Run quietly - uv only outputs when changes are needed
- uv sync --all-extras --quiet 2>/dev/null || uv sync --all-extras
+ uv sync --group dev --quiet 2>/dev/null || uv sync --group dev
# Activate venv by setting environment variables directly
# This works reliably for both interactive shells and `nix develop --command`
diff --git a/library/jobs/spec_driven_development/steps/clarify.md b/library/jobs/spec_driven_development/steps/clarify.md
index 5d8d53da..379defb5 100644
--- a/library/jobs/spec_driven_development/steps/clarify.md
+++ b/library/jobs/spec_driven_development/steps/clarify.md
@@ -8,7 +8,7 @@ Resolve ambiguities, fill gaps, and validate completeness of the specification t
Review the existing specification, identify underspecified areas, and ask structured questions to resolve them. Update the spec.md with clarifications.
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
**Critical**: This step refines requirements, not implementation. Do not add code examples or technical solutions. Keep clarifications focused on user needs, acceptance criteria, and behavior - not how things will be coded.
diff --git a/library/jobs/spec_driven_development/steps/constitution.md b/library/jobs/spec_driven_development/steps/constitution.md
index 11ab7c70..21c8933e 100644
--- a/library/jobs/spec_driven_development/steps/constitution.md
+++ b/library/jobs/spec_driven_development/steps/constitution.md
@@ -8,7 +8,7 @@ Create a foundational governance document (`constitution.md`) that establishes p
Guide the user through defining their project's constitution by asking structured questions about their development priorities, quality standards, and governance preferences.
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
**Critical**: This step captures principles and standards, not implementation code. The constitution describes what technologies and patterns to use, not how to code them. Do not include code examples - those belong only in the implement step.
diff --git a/library/jobs/spec_driven_development/steps/plan.md b/library/jobs/spec_driven_development/steps/plan.md
index 1d4c6b6e..4aca4e93 100644
--- a/library/jobs/spec_driven_development/steps/plan.md
+++ b/library/jobs/spec_driven_development/steps/plan.md
@@ -8,7 +8,7 @@ Create a comprehensive technical implementation plan that defines architecture,
Analyze the clarified specification and create detailed technical planning documents that will guide implementation.
-**Important**: Use the AskUserQuestion tool to ask structured questions when technology choices need user input.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
**Critical**: This step produces planning documents, NOT implementation code. Do not write actual code - that happens in the implement step. You may include:
- API contracts (endpoints, request/response schemas)
diff --git a/library/jobs/spec_driven_development/steps/specify.md b/library/jobs/spec_driven_development/steps/specify.md
index 6cb0a597..ad24cd55 100644
--- a/library/jobs/spec_driven_development/steps/specify.md
+++ b/library/jobs/spec_driven_development/steps/specify.md
@@ -8,7 +8,7 @@ Create a functional specification (`spec.md`) that defines what the feature shou
Guide the user through creating a comprehensive specification by asking structured questions about their feature requirements, user needs, and success criteria.
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
**Key Principle**: This step focuses entirely on the **"what"** and **"why"** - never the **"how"**. Technology choices and implementation details belong in the planning step.
@@ -212,10 +212,7 @@ User: Users need accounts to save their preferences and access their data across
Claude: That makes sense. Who are the primary users of this authentication system?
-[Uses AskUserQuestion with options: New users signing up, Returning users logging in,
-Admins managing users, All of the above]
-
-User: All of the above
+User: New users signing up, returning users logging in, and admins managing users.
Claude: Let's create user stories for each. Starting with new users signing up:
diff --git a/pr_review/deepwork-jobs/review.md b/pr_review/deepwork-jobs/review.md
new file mode 100644
index 00000000..e1ca35bb
--- /dev/null
+++ b/pr_review/deepwork-jobs/review.md
@@ -0,0 +1,149 @@
+# deepwork-jobs Expert Review
+
+**PR**: #192 - feat: Add experts system for auto-improving domain knowledge
+**Date**: 2026-02-01
+**Reviewer**: deepwork-jobs expert
+
+## Summary
+
+The experts system is a well-designed addition to DeepWork that follows established patterns from the jobs system. The architecture cleanly separates concerns across schema definition (`expert_schema.py`), parsing (`experts_parser.py`), generation (`experts_generator.py`), and CLI (`experts.py`). The use of dynamic command embedding (`$(deepwork topics ...)`) in the generated agent template is an elegant solution for keeping expert agents up-to-date without regenerating them.
+
+From a DeepWork Jobs perspective, this integration is solid. The experts system complements jobs by providing domain knowledge that can be leveraged during job execution. The sync command properly handles both jobs and experts, and the naming conventions (`dwe_` prefix for expert agents) avoid conflicts with job-generated skills.
+
+## Issues Found
+
+### Issue 1
+- **File**: `src/deepwork/schemas/expert_schema.py`
+- **Line(s)**: 22, 50, 75
+- **Severity**: Minor
+- **Issue**: The `additionalProperties: False` constraint on all three schemas is strict and may cause friction when evolving the schema. For example, if users want to add custom metadata fields to topics or learnings (like `author`, `tags`, or `priority`), they would get validation errors.
+- **Suggestion**: Consider whether this strictness is intentional. If extensibility is desired, either remove `additionalProperties: False` or document clearly that custom fields are not supported. The jobs schema (`JOB_SCHEMA`) also uses `additionalProperties: False`, so this is consistent with existing patterns.
+
+### Issue 2
+- **File**: `src/deepwork/core/experts_parser.py`
+- **Line(s)**: 377-379, 387-389
+- **Severity**: Minor
+- **Issue**: The exception handling for topic/learning parsing re-raises `ExpertParseError` with a bare `raise`. This works but loses context about which file caused the error. The error message is already set in `parse_topic_file`/`parse_learning_file`, but the pattern is unusual.
+- **Suggestion**: Consider either removing the try/except entirely (letting errors propagate naturally) or adding file context to the error message.
+
+### Issue 3
+- **File**: `src/deepwork/core/experts_generator.py`
+- **Line(s)**: 77-78
+- **Severity**: Suggestion
+- **Issue**: The `_build_expert_context` method includes `topics_count` and `learnings_count` but these are not used in the template (`agent-expert.md.jinja`). This is dead code that could confuse future maintainers.
+- **Suggestion**: Either remove these unused context variables or add them to the template if there is a future use case (e.g., showing counts in agent description).
+
+### Issue 4
+- **File**: `src/deepwork/templates/claude/agent-expert.md.jinja`
+- **Line(s)**: 14
+- **Severity**: Minor
+- **Issue**: The `truncate(200)` filter is applied after `replace('\n', ' ')`, which means the 200-character limit includes any spaces that replaced newlines. A multiline description might get truncated unexpectedly short if it has many newlines.
+- **Suggestion**: This is likely acceptable behavior, but consider if `truncate(200, killwords=False, end='...')` would be better for cleaner truncation at word boundaries.
+
+### Issue 5
+- **File**: `src/deepwork/cli/sync.py`
+- **Line(s)**: (expert agent generation section)
+- **Severity**: Minor
+- **Issue**: Expert agent generation is hardcoded to only run for Claude (`adapter.name == "claude"`). The comment says "agents live in .claude/agents/" but this should be abstracted through the adapter if/when other platforms support agents.
+- **Suggestion**: Consider adding an `adapter.supports_agents` property or similar abstraction so this check is not a string comparison. However, this is acceptable for now given Claude is the only platform with agent support.
+
+### Issue 6
+- **File**: `src/deepwork/core/experts_parser.py`
+- **Line(s)**: 61
+- **Severity**: Minor
+- **Issue**: The frontmatter regex pattern allows optional trailing content after the closing `---` on the same line, but then requires the closing `---` to be at the start of a line. This could cause parsing issues with edge cases.
+- **Suggestion**: The current pattern works correctly for well-formed files. Consider adding a test case for edge cases like trailing whitespace after the closing `---`.
+
+### Issue 7
+- **File**: `src/deepwork/standard/experts/deepwork_jobs/expert.yml`
+- **Line(s)**: 151
+- **Severity**: Suggestion
+- **Issue**: The expert documentation mentions "Claude Code currently only supports script hooks. Prompt hooks are parsed but not executed (documented limitation)." This is valuable but might become stale if prompt hooks are implemented.
+- **Suggestion**: The learning file `prompt_hooks_not_executed.md` already exists in learnings/ which is the right approach. Consider referencing it or adding a note about checking current platform capabilities.
+
+## Code Suggestions
+
+### Suggestion 1: Remove unused context variables
+
+**File**: `src/deepwork/core/experts_generator.py`
+
+Before:
+```python
+def _build_expert_context(self, expert: ExpertDefinition) -> dict:
+ return {
+ "expert_name": expert.name,
+ "discovery_description": expert.discovery_description,
+ "full_expertise": expert.full_expertise,
+ "topics_count": len(expert.topics),
+ "learnings_count": len(expert.learnings),
+ }
+```
+
+After:
+```python
+def _build_expert_context(self, expert: ExpertDefinition) -> dict:
+ return {
+ "expert_name": expert.name,
+ "discovery_description": expert.discovery_description,
+ "full_expertise": expert.full_expertise,
+ }
+```
+
+### Suggestion 2: Simplify redundant exception handling
+
+**File**: `src/deepwork/core/experts_parser.py`
+
+Before:
+```python
+for topic_file in topics_dir.glob("*.md"):
+ try:
+ topic = parse_topic_file(topic_file)
+ topics.append(topic)
+ except ExpertParseError:
+ raise
+```
+
+After:
+```python
+for topic_file in topics_dir.glob("*.md"):
+ topic = parse_topic_file(topic_file)
+ topics.append(topic)
+```
+
+The try/except here adds no value since it just re-raises. The `parse_topic_file` function already includes the filename in its error messages.
+
+### Suggestion 3: Add platform abstraction for agent support
+
+**File**: `src/deepwork/core/adapters.py` (add property to `AgentAdapter` base class):
+
+```python
+@property
+def supports_agents(self) -> bool:
+ """Whether this platform supports expert agents."""
+ return False
+
+# In ClaudeAdapter:
+@property
+def supports_agents(self) -> bool:
+ return True
+```
+
+Then in `sync.py`:
+```python
+if experts and adapter.supports_agents:
+```
+
+## Approval Status
+
+**APPROVED**: No blocking issues
+
+The experts system is well-implemented and follows DeepWork's established patterns. The issues identified are minor improvements that do not block merging. The system correctly:
+
+1. Defines clear schemas for expert, topic, and learning validation
+2. Parses expert definitions including nested topics and learnings
+3. Generates agent files with dynamic command embedding for up-to-date content
+4. Integrates with the sync command to generate expert agents alongside job skills
+5. Provides CLI commands (`deepwork topics`, `deepwork learnings`) for dynamic content retrieval
+6. Has comprehensive test coverage across unit and integration tests
+
+The architecture cleanly separates the experts system from jobs while allowing them to coexist and complement each other.
diff --git a/pr_review/deepwork-rules/review.md b/pr_review/deepwork-rules/review.md
new file mode 100644
index 00000000..23fe0279
--- /dev/null
+++ b/pr_review/deepwork-rules/review.md
@@ -0,0 +1,95 @@
+# deepwork-rules Review
+
+**PR**: #197
+**Date**: 2026-02-01
+**Reviewer**: deepwork-rules expert
+
+## Summary
+
+This PR performs a significant architectural restructuring of the DeepWork rules system. The changes migrate `deepwork_rules` from a "standard job" under `src/deepwork/standard_jobs/` to a "standard expert" under `src/deepwork/standard/experts/`. The migration involves:
+
+1. **Directory relocation**: Moving from `standard_jobs/deepwork_rules/` to `standard/experts/deepwork_rules/`
+2. **Schema change**: Converting from `job.yml` format to `workflow.yml` format, with workflows nested under the expert structure
+3. **Naming convention update**: The skill name changed from `deepwork_rules.define` to `deepwork-rules.define` (underscores to dashes)
+4. **Instruction update**: Removing reference to `AskUserQuestion` tool in favor of simpler "ask questions one at a time" guidance
+
+From a rules system perspective, the core functionality (detection modes, pattern syntax, comparison modes, hooks) remains intact and well-documented. The rules themselves in `.deepwork/rules/` continue to work the same way.
+
+## Issues Found
+
+### Issue 1
+- **File**: `src/deepwork/standard/experts/deepwork_rules/workflows/define/steps/define.md`
+- **Line(s)**: 9
+- **Severity**: Minor
+- **Issue**: The instruction text "Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list." is less explicit than the previous `AskUserQuestion` tool reference. While the previous version referenced a specific tool that may no longer exist, the new wording could be more precise about the expected UX pattern.
+- **Suggestion**: Consider whether this instruction achieves the desired interaction pattern. If Claude Code has an `AskUserQuestion` tool or similar mechanism, referencing it directly would provide clearer guidance. If no such tool exists, the current wording is acceptable but could benefit from mentioning that the agent should use natural conversational turn-taking.
+
+### Issue 2
+- **File**: `src/deepwork/standard/experts/deepwork_rules/expert.yml`
+- **Line(s)**: 67 (end of file)
+- **Severity**: Minor
+- **Issue**: The `full_expertise` field provides a good overview of rules but lacks documentation about the `action` field in rules (e.g., `action: command` for running shell commands like `uv sync` when `pyproject.toml` changes). The old `job.yml` description mentioned this feature: "Command action rules: Execute their command (e.g., `uv sync`) when the agent stops".
+- **Suggestion**: Add documentation about command-action rules to the `full_expertise` section. This is an important feature that allows rules to execute shell commands rather than just prompting the agent.
+
+### Issue 3
+- **File**: `src/deepwork/standard/experts/deepwork_rules/workflows/define/steps/define.md`
+- **Line(s)**: 103-143 (Step 6 section)
+- **Severity**: Minor
+- **Issue**: The rule file format examples do not document the `action` field for command-based rules. Users cannot learn how to create rules that automatically run commands (like `uv sync`) when certain files change.
+- **Suggestion**: Add an example for command-action rules.
+
+### Issue 4
+- **File**: `src/deepwork/standard/experts/deepwork_rules/workflows/define/workflow.yml`
+- **Line(s)**: 1-21
+- **Severity**: Suggestion
+- **Issue**: The workflow.yml only has one step (`define`) which makes the workflow structure somewhat redundant. The old `job.yml` was simpler for this single-step job.
+- **Suggestion**: This is acceptable given the broader architectural shift to the experts system. Single-step workflows are a valid pattern.
+
+## Code Suggestions
+
+### Suggestion 1: Adding Command Action Documentation to expert.yml
+
+**File**: src/deepwork/standard/experts/deepwork_rules/expert.yml
+
+Before:
+```yaml
+ ## When to Use Rules
+
+ Rules are most valuable for:
+ - Keeping documentation in sync with code
+ - Enforcing security reviews for sensitive changes
+ - Ensuring test coverage follows source changes
+ - Maintaining team guidelines automatically
+```
+
+After:
+```yaml
+ ## Action Types
+
+ Rules support two action types:
+
+ - **prompt** (default): Show instructions to the agent
+ - **command**: Execute a shell command automatically
+
+ Command actions are useful for:
+ - Auto-running `uv sync` when pyproject.toml changes
+ - Running linters when source files change
+ - Generating documentation when schemas change
+
+ ## When to Use Rules
+
+ Rules are most valuable for:
+ - Keeping documentation in sync with code
+ - Enforcing security reviews for sensitive changes
+ - Ensuring test coverage follows source changes
+ - Maintaining team guidelines automatically
+ - Auto-running commands when specific files change
+```
+
+**Rationale**: The command action feature is a powerful capability that differentiates DeepWork rules from simple trigger-based reminders. Users should be aware this option exists when the expert is consulted.
+
+## Approval Status
+
+**APPROVED**
+
+No blocking issues within my domain. The migration preserves all core rules system functionality (detection modes, pattern syntax, comparison modes, hooks configuration). The rules continue to work correctly with the same file format and location (`.deepwork/rules/`). The suggestions above are improvements that would enhance documentation completeness, but the system is functional as-is.
diff --git a/pr_review/experts/review.md b/pr_review/experts/review.md
new file mode 100644
index 00000000..e3e19179
--- /dev/null
+++ b/pr_review/experts/review.md
@@ -0,0 +1,117 @@
+# experts Review
+
+**PR**: #197
+**Date**: 2026-02-01
+**Reviewer**: experts expert
+
+## Summary
+
+This PR merges the jobs system into the experts framework, creating a unified architecture where workflows are now owned by experts rather than being standalone entities. The change represents a significant restructuring:
+
+1. **Terminology shift**: "job" -> "workflow", "job.yml" -> "workflow.yml"
+2. **Structural change**: Workflows now live inside expert directories (`.deepwork/experts/[expert]/workflows/[workflow]/`) rather than separately (`.deepwork/jobs/[job]/`)
+3. **Schema evolution**: `job_schema.py` renamed to `workflow_schema.py`, with appropriate field updates
+4. **Skill naming**: Skills now use `expert-name.step-id` format instead of `job-name.step-id`
+
+The overall architecture is sound - embedding workflows within experts creates a cohesive domain knowledge + action system. The expert.yml schema remains minimal (just `discovery_description` and `full_expertise`), while workflow.yml carries the complex step definitions. Topics and learnings continue to follow the documented frontmatter patterns.
+
+## Issues Found
+
+### Issue 1
+- **File**: `src/deepwork/cli/install.py`
+- **Line(s)**: 438-439
+- **Severity**: Minor
+- **Issue**: The "Next steps" output message still references the old naming convention `/deepwork-jobs.define`. This should be updated to reflect the new workflow naming under the experts system.
+- **Suggestion**: Update line 438 to say `"/experts.define"` or `"/experts.new_workflow"` to match the new skill naming convention.
+
+### Issue 2
+- **File**: `src/deepwork/standard/experts/experts/workflows/new_workflow/workflow.yml`
+- **Line(s)**: 35
+- **Severity**: Major
+- **Issue**: The `review_workflow_spec` step has `agent: general-purpose`, but `general-purpose` is not a defined expert. According to the step delegation topic, valid agent values should reference actual experts. Using `general-purpose` will cause the skill generation to include `agent: general-purpose` in the frontmatter, which will fail at runtime when Claude Code tries to load a non-existent expert agent.
+- **Suggestion**: Either remove the `agent` field from this step (letting it run in the main context) or set it to `experts` if the expert knowledge is needed. The `quality_criteria` field alone triggers sub-agent validation without requiring expert context.
+
+### Issue 3
+- **File**: `src/deepwork/standard/experts/experts/topics/workflow_yml_schema.md`
+- **Line(s)**: 10
+- **Severity**: Suggestion
+- **Issue**: The topic has "workflow" as a keyword, but according to the keyword guidelines, topics should avoid broad domain terms. Since this is a topic within the "experts" expert which is about the experts system (including workflows), "workflow" is arguably too broad.
+- **Suggestion**: Consider removing "workflow" and using more specific keywords like "workflow.yml", "specification", "required fields".
+
+### Issue 4
+- **File**: `src/deepwork/standard/experts/experts/expert.yml`
+- **Line(s)**: 232-234
+- **Severity**: Minor
+- **Issue**: The Skill Generation section documents step skills but doesn't mention workflow meta-skills which also get generated (`expert-name.workflow-name/SKILL.md`).
+- **Suggestion**: Add a line clarifying workflow meta-skill naming: "Workflow meta-skill: `.claude/skills/[expert-name].[workflow-name]/SKILL.md`"
+
+### Issue 5
+- **File**: `src/deepwork/standard/experts/experts/expert.yml`
+- **Line(s)**: 299-305
+- **Severity**: Suggestion
+- **Issue**: The "Common Patterns" section references `/experts.define` and `/experts.implement` but could be confusing since there are multiple workflows.
+- **Suggestion**: Add clarification that `/experts` without a step suffix invokes the `new_workflow` meta-skill.
+
+### Issue 6
+- **File**: `src/deepwork/templates/claude/skill-workflow-meta.md.jinja`
+- **Line(s)**: 90
+- **Severity**: Minor
+- **Issue**: The template uses `expert_name | replace('-', '_')` to convert back to folder naming. This transformation works for simple cases but could break with edge cases.
+- **Suggestion**: Consider having the generator pass the original folder name to the template.
+
+## Code Suggestions
+
+### Suggestion 1: Fix outdated install message
+
+**File**: `src/deepwork/cli/install.py`
+
+Before:
+```python
+ console.print("[bold]Next steps:[/bold]")
+ console.print(" 1. Start your agent CLI (ex. [cyan]claude[/cyan] or [cyan]gemini[/cyan])")
+ console.print(" 2. Define your first workflow using [cyan]/deepwork-jobs.define[/cyan]")
+```
+
+After:
+```python
+ console.print("[bold]Next steps:[/bold]")
+ console.print(" 1. Start your agent CLI (ex. [cyan]claude[/cyan] or [cyan]gemini[/cyan])")
+ console.print(" 2. Define your first workflow using [cyan]/experts.define[/cyan]")
+```
+
+**Rationale**: The skill naming has changed from `deepwork-jobs.step` to `experts.step` as workflows are now owned by experts.
+
+### Suggestion 2: Fix invalid agent reference in workflow
+
+**File**: `src/deepwork/standard/experts/experts/workflows/new_workflow/workflow.yml`
+
+Before:
+```yaml
+ - id: review_workflow_spec
+ name: "Review Workflow Specification"
+ description: "Review workflow.yml against quality criteria using a sub-agent for unbiased validation"
+ instructions_file: steps/review_workflow_spec.md
+ ...
+ agent: general-purpose
+ quality_criteria:
+ - "Workflow name is lowercase with underscores, no spaces or special characters"
+```
+
+After:
+```yaml
+ - id: review_workflow_spec
+ name: "Review Workflow Specification"
+ description: "Review workflow.yml against quality criteria using a sub-agent for unbiased validation"
+ instructions_file: steps/review_workflow_spec.md
+ ...
+ quality_criteria:
+ - "Workflow name is lowercase with underscores, no spaces or special characters"
+```
+
+**Rationale**: `general-purpose` is not a valid expert name. The `quality_criteria` field alone triggers sub-agent validation without requiring expert context, which achieves the stated goal of "unbiased validation."
+
+## Approval Status
+
+**CHANGES_REQUESTED**
+
+The invalid `agent: general-purpose` reference in the new_workflow workflow.yml is a blocking issue that will cause runtime failures when the generated skill tries to load a non-existent expert. This must be fixed before merging.
diff --git a/pr_review/final_summary.md b/pr_review/final_summary.md
new file mode 100644
index 00000000..4219f33c
--- /dev/null
+++ b/pr_review/final_summary.md
@@ -0,0 +1,58 @@
+# PR Review Final Summary
+
+**PR**: #197 - Merge jobs into experts workflows
+**Total Iterations**: 1
+**Final Status**: APPROVED
+
+## Review Timeline
+
+| Iteration | Date | Issues Addressed | Remaining |
+|-----------|------|------------------|-----------|
+| 1 | 2026-02-01 | 10 total (4 fixed, 6 skipped with reason) | 0 |
+
+## Expert Final Status
+
+| Expert | Final Status |
+|--------|--------------|
+| deepwork-rules | APPROVED |
+| experts | APPROVED |
+
+## Issue Resolution Summary
+
+| Expert | Total Issues | Fixed | Skipped (with reason) |
+|--------|--------------|-------|----------------------|
+| deepwork-rules | 4 | 2 | 2 |
+| experts | 6 | 2 | 4 |
+
+## Key Improvements Made
+
+1. **Fixed install CLI message**: Updated from `/deepwork-jobs.define` to `/experts.define`
+2. **Added command-action documentation**: Documented the `action: command` feature in deepwork_rules expert.yml
+3. **Added command-action examples**: Added format and example for command actions in define.md
+4. **Improved skill generation docs**: Explained both step skills and workflow meta-skills in experts expert.yml
+5. **Enhanced review_pr workflow**: Added quality criteria requiring complete issue accounting
+
+## Skipped Items (with justification)
+
+### False Positives
+- **`agent: general-purpose` invalid**: This is actually a valid Claude Code built-in agent type, not a DeepWork expert reference
+
+### Intentional Design Decisions
+- **Question-asking instruction change**: Simpler guidance works better across platforms
+- **Single-step workflow structure**: Acceptable trade-off for unified architecture
+
+### Low Impact / Deferred
+- **"workflow" keyword breadth**: Acceptable for discoverability
+- **Meta-skill routing clarification**: Low user impact
+- **Template folder name reverse-engineering**: Edge cases are rare
+
+## Process Improvements
+
+Added quality criteria to `improve_and_rereview` step requiring:
+- Complete issue accounting (every issue must be FIXED or SKIPPED with reason)
+- No silent omissions
+- Explicit documentation of all resolutions
+
+## Next Steps
+
+PR is ready to merge. All 630 tests pass.
diff --git a/pr_review/iteration_1/summary.md b/pr_review/iteration_1/summary.md
new file mode 100644
index 00000000..34ae49e2
--- /dev/null
+++ b/pr_review/iteration_1/summary.md
@@ -0,0 +1,54 @@
+# Iteration 1 Summary
+
+**Date**: 2026-02-01
+**Iteration**: 1 of 3 max
+
+## Feedback Addressed
+
+**IMPORTANT**: All issues from expert reviews are listed - none silently omitted.
+
+### From deepwork-rules expert
+
+| Issue | Severity | Status | Resolution |
+|-------|----------|--------|------------|
+| Question-asking instruction less explicit (define.md line 9) | Minor | SKIPPED | Intentional change - simpler guidance works across platforms |
+| Missing command-action docs in expert.yml | Minor | FIXED | Added "Action Types" section with command action format and examples |
+| Missing command-action example in define.md | Minor | FIXED | Added command action format in Step 6 and example in Examples section |
+| Single-step workflow redundant | Suggestion | SKIPPED | Acceptable trade-off for unified architecture |
+
+### From experts expert
+
+| Issue | Severity | Status | Resolution |
+|-------|----------|--------|------------|
+| Install CLI references old `/deepwork-jobs.define` | Minor | FIXED | Updated to `/experts.define` in install.py |
+| Invalid `agent: general-purpose` reference | Major | SKIPPED | FALSE POSITIVE - `general-purpose` is a valid Claude Code built-in agent type |
+| "workflow" keyword too broad in topic | Suggestion | SKIPPED | Acceptable - helps with discoverability |
+| Missing workflow meta-skill documentation | Minor | FIXED | Added detailed explanation of step skills vs workflow meta-skills |
+| Clarify meta-skill routing in "Common Patterns" | Suggestion | SKIPPED | Deferred - low impact |
+| Template reverse-engineers folder name | Minor | SKIPPED | Known limitation - edge cases rare |
+
+## Changes Made
+
+1. **src/deepwork/cli/install.py**: Updated "Next steps" message from `/deepwork-jobs.define` to `/experts.define`
+2. **src/deepwork/standard/experts/deepwork_rules/expert.yml**: Added "Action Types" section documenting command actions
+3. **src/deepwork/standard/experts/deepwork_rules/workflows/define/steps/define.md**: Added command action format and example
+4. **src/deepwork/standard/experts/experts/expert.yml**: Expanded Skill Generation section to explain both step skills and workflow meta-skills
+5. **src/deepwork/standard/experts/experts/workflows/review_pr/steps/improve_and_rereview.md**: Added quality criteria requiring complete issue accounting
+
+## Re-Review Results
+
+| Expert | Previous Status | New Status | Remaining Issues |
+|--------|-----------------|------------|------------------|
+| deepwork-rules | APPROVED | APPROVED | 0 (2 suggestions skipped with reason) |
+| experts | CHANGES_REQUESTED | APPROVED | 0 (1 false positive, 2 suggestions skipped) |
+
+## Outcome
+
+**COMPLETE**: All blocking issues resolved. All issues explicitly accounted for with fix or documented reason for skipping. PR ready to merge.
+
+## Issue Resolution Summary
+
+| Expert | Total Issues | Fixed | Skipped (with reason) |
+|--------|--------------|-------|----------------------|
+| deepwork-rules | 4 | 2 | 2 (intentional design, acceptable trade-off) |
+| experts | 6 | 2 | 4 (1 false positive, 3 low-impact/deferred) |
diff --git a/pr_review/relevance_assessments.md b/pr_review/relevance_assessments.md
new file mode 100644
index 00000000..3f8f09f9
--- /dev/null
+++ b/pr_review/relevance_assessments.md
@@ -0,0 +1,155 @@
+# PR Relevance Assessment
+
+**PR**: #197 - Merge jobs into experts workflows
+**Branch**: total_shift
+**Date**: 2026-02-01
+
+## Changed Files
+
+- .claude/agents/dwe_deepwork-jobs.md
+- .claude/agents/dwe_experts.md
+- .claude/settings.json
+- .claude/skills/add_platform/SKILL.md
+- .claude/skills/commit/SKILL.md
+- .claude/skills/deepwork_jobs.define/SKILL.md
+- .claude/skills/deepwork_jobs.implement/SKILL.md
+- .claude/skills/deepwork_jobs.learn/SKILL.md
+- .claude/skills/deepwork_jobs.review_job_spec/SKILL.md
+- .claude/skills/deepwork_jobs/SKILL.md
+- .claude/skills/deepwork_rules.define/SKILL.md
+- .claude/skills/deepwork_rules/SKILL.md
+- .claude/skills/manual_tests/SKILL.md
+- .claude/skills/review_pr.check_relevance/SKILL.md
+- .claude/skills/review_pr.deep_review/SKILL.md
+- .claude/skills/review_pr.improve_and_rereview/SKILL.md
+- .claude/skills/review_pr/SKILL.md
+- .claude/skills/update/SKILL.md
+- .deepwork/experts/deepwork_jobs/expert.yml
+- .deepwork/experts/deepwork_jobs/learnings/.gitkeep
+- .deepwork/experts/deepwork_jobs/learnings/prompt_hooks_not_executed.md
+- .deepwork/experts/deepwork_jobs/topics/hooks_and_validation.md
+- .deepwork/experts/deepwork_jobs/topics/job_yml_schema.md
+- .deepwork/experts/deepwork_jobs/topics/skill_generation.md
+- .deepwork/experts/deepwork_jobs/topics/step_delegation.md
+- .deepwork/experts/deepwork_jobs/topics/step_instructions.md
+- .deepwork/experts/experts/expert.yml
+- .deepwork/experts/experts/learnings/.gitkeep
+- .deepwork/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md
+- .deepwork/experts/experts/learnings/keep_experts_focused.md
+- .deepwork/experts/experts/learnings/review_pr_efficiency_improvements.md
+- .deepwork/experts/experts/topics/discovery_descriptions.md
+- .deepwork/experts/experts/topics/expert_design_patterns.md
+- .deepwork/jobs/deepwork_jobs/job.yml
+- .deepwork/jobs/deepwork_jobs/steps/define.md
+- .deepwork/jobs/deepwork_jobs/steps/implement.md
+- .deepwork/jobs/deepwork_jobs/steps/learn.md
+- .deepwork/jobs/deepwork_jobs/steps/review_job_spec.md
+- .deepwork/jobs/deepwork_rules/steps/define.md
+- .deepwork/jobs/review_pr/job.yml
+- .deepwork/jobs/review_pr/steps/check_relevance.md
+- .deepwork/jobs/review_pr/steps/deep_review.md
+- .deepwork/jobs/review_pr/steps/improve_and_rereview.md
+- .gemini/skills/deepwork_jobs/define.toml
+- .gemini/skills/deepwork_jobs/implement.toml
+- .gemini/skills/deepwork_jobs/learn.toml
+- .gemini/skills/deepwork_jobs/review_job_spec.toml
+- .gemini/skills/deepwork_rules/define.toml
+- .gemini/skills/review_pr/check_relevance.toml
+- .gemini/skills/review_pr/deep_review.toml
+- .gemini/skills/review_pr/improve_and_rereview.toml
+- .gemini/skills/review_pr/index.toml
+- CHANGELOG.md
+- README.md
+- doc/architecture.md
+- doc/experts_requirements.md
+- flake.nix
+- library/jobs/spec_driven_development/steps/clarify.md
+- library/jobs/spec_driven_development/steps/constitution.md
+- library/jobs/spec_driven_development/steps/plan.md
+- library/jobs/spec_driven_development/steps/specify.md
+- pyproject.toml
+- src/deepwork/cli/experts.py
+- src/deepwork/cli/install.py
+- src/deepwork/cli/main.py
+- src/deepwork/cli/sync.py
+- src/deepwork/core/experts_generator.py
+- src/deepwork/core/experts_parser.py
+- src/deepwork/schemas/expert_schema.py
+- src/deepwork/standard/experts/deepwork_jobs/expert.yml
+- src/deepwork/standard/experts/deepwork_jobs/learnings/.gitkeep
+- src/deepwork/standard/experts/deepwork_jobs/learnings/prompt_hooks_not_executed.md
+- src/deepwork/standard/experts/deepwork_jobs/topics/hooks_and_validation.md
+- src/deepwork/standard/experts/deepwork_jobs/topics/job_yml_schema.md
+- src/deepwork/standard/experts/deepwork_jobs/topics/skill_generation.md
+- src/deepwork/standard/experts/deepwork_jobs/topics/step_delegation.md
+- src/deepwork/standard/experts/deepwork_jobs/topics/step_instructions.md
+- src/deepwork/standard/experts/experts/expert.yml
+- src/deepwork/standard/experts/experts/learnings/.gitkeep
+- src/deepwork/standard/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md
+- src/deepwork/standard/experts/experts/learnings/keep_experts_focused.md
+- src/deepwork/standard/experts/experts/learnings/review_pr_efficiency_improvements.md
+- src/deepwork/standard/experts/experts/topics/discovery_descriptions.md
+- src/deepwork/standard/experts/experts/topics/expert_design_patterns.md
+- src/deepwork/standard_jobs/deepwork_jobs/job.yml
+- src/deepwork/standard_jobs/deepwork_jobs/steps/define.md
+- src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md
+- src/deepwork/standard_jobs/deepwork_jobs/steps/learn.md
+- src/deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md
+- src/deepwork/standard_jobs/deepwork_rules/steps/define.md
+- src/deepwork/standard_jobs/review_pr/job.yml
+- src/deepwork/standard_jobs/review_pr/steps/check_relevance.md
+- src/deepwork/standard_jobs/review_pr/steps/deep_review.md
+- src/deepwork/standard_jobs/review_pr/steps/improve_and_rereview.md
+- src/deepwork/templates/claude/agent-expert.md.jinja
+- src/deepwork/templates/claude/skill-job-meta.md.jinja
+- tests/integration/test_experts_sync.py
+- tests/unit/test_expert_schema.py
+- tests/unit/test_experts_cli.py
+- tests/unit/test_experts_generator.py
+- tests/unit/test_experts_parser.py
+- uv.lock
+
+## Expert Assessments
+
+### deepwork-rules
+**Status**: RELEVANT
+**Justification**: This PR modifies the `deepwork_rules.define` step instruction file, which directly governs how file-change rules are created. While the change is minor (updating guidance on how to ask questions), it affects the user experience when defining new rules.
+**Relevant files**:
+- `src/deepwork/standard_jobs/deepwork_rules/steps/define.md`
+- `.claude/skills/deepwork_rules.define/SKILL.md`
+- `.claude/skills/deepwork_rules/SKILL.md`
+- `.deepwork/jobs/deepwork_rules/steps/define.md`
+
+### experts
+**Status**: RELEVANT
+**Justification**: This PR directly implements the experts system that is core to this domain. It introduces the expert.yml schema, topics and learnings structure, workflow integration, skill generation from experts, and CLI commands for managing experts - all fundamental concepts of the DeepWork experts framework.
+**Relevant files**:
+- `src/deepwork/standard/experts/experts/expert.yml`
+- `src/deepwork/standard/experts/deepwork_jobs/expert.yml`
+- `.deepwork/experts/*/expert.yml`
+- `src/deepwork/schemas/expert_schema.py`
+- `src/deepwork/standard/experts/experts/topics/*.md`
+- `src/deepwork/standard/experts/experts/learnings/*.md`
+- `src/deepwork/standard/experts/deepwork_jobs/topics/*.md`
+- `src/deepwork/standard/experts/deepwork_jobs/learnings/*.md`
+- `src/deepwork/core/experts_parser.py`
+- `src/deepwork/core/experts_generator.py`
+- `src/deepwork/cli/experts.py`
+- `src/deepwork/cli/sync.py`
+- `src/deepwork/cli/install.py`
+- `src/deepwork/templates/claude/agent-expert.md.jinja`
+- `.claude/agents/dwe_experts.md`
+- `.claude/agents/dwe_deepwork-jobs.md`
+- `.claude/skills/*/SKILL.md`
+- `tests/unit/test_expert_schema.py`
+- `tests/unit/test_experts_cli.py`
+- `tests/unit/test_experts_generator.py`
+- `tests/unit/test_experts_parser.py`
+- `tests/integration/test_experts_sync.py`
+- `doc/experts_requirements.md`
+- `doc/architecture.md`
+
+## Summary
+
+**Relevant experts**: deepwork-rules, experts
+**Next step**: Run `/experts.deep_review` with these experts
diff --git a/pr_review/review_summary.md b/pr_review/review_summary.md
new file mode 100644
index 00000000..726d68bd
--- /dev/null
+++ b/pr_review/review_summary.md
@@ -0,0 +1,35 @@
+# PR Review Summary
+
+**PR**: #197 - Merge jobs into experts workflows
+**Date**: 2026-02-01
+
+## Expert Reviews
+
+| Expert | Domain | Status | Critical | Major | Minor | Suggestions |
+|--------|--------|--------|----------|-------|-------|-------------|
+| deepwork-rules | File-change rules system | APPROVED | 0 | 0 | 3 | 1 |
+| experts | Experts system architecture | CHANGES_REQUESTED | 0 | 1 | 3 | 2 |
+
+## Key Themes
+
+1. **Migration successful**: Both experts confirm the core functionality is preserved through the jobs-to-workflows migration
+2. **Documentation gaps**: Missing documentation for command-action rules and workflow meta-skills
+3. **Invalid agent reference**: The `agent: general-purpose` in new_workflow/workflow.yml is not a valid expert name
+4. **Outdated messaging**: The install CLI still references old `/deepwork-jobs.define` naming
+
+## Blocking Issues
+
+### 1. Invalid agent reference (Major)
+- **File**: `src/deepwork/standard/experts/experts/workflows/new_workflow/workflow.yml`
+- **Issue**: `agent: general-purpose` is not a defined expert
+- **Fix**: Remove the `agent` field; `quality_criteria` alone triggers sub-agent validation
+
+## Overall Status
+
+**CHANGES_REQUESTED**
+
+The experts expert requested changes due to a blocking issue. The deepwork-rules expert approved with suggestions.
+
+## Next Steps
+
+Run `/experts.improve_and_rereview` to address the feedback and get re-review.
diff --git a/pyproject.toml b/pyproject.toml
index c2bc3e4a..d62ee237 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "deepwork"
-version = "0.5.1"
+version = "0.6.0"
description = "Framework for enabling AI agents to perform complex, multi-step work tasks"
readme = "README.md"
requires-python = ">=3.11"
@@ -38,6 +38,16 @@ dev = [
"types-PyYAML",
]
+[dependency-groups]
+dev = [
+ "pytest>=7.0",
+ "pytest-mock>=3.10",
+ "pytest-cov>=4.0",
+ "ruff>=0.1.0",
+ "mypy>=1.0",
+ "types-PyYAML",
+]
+
[project.scripts]
deepwork = "deepwork.cli.main:cli"
diff --git a/src/deepwork/cli/experts.py b/src/deepwork/cli/experts.py
new file mode 100644
index 00000000..1d27da8f
--- /dev/null
+++ b/src/deepwork/cli/experts.py
@@ -0,0 +1,152 @@
+"""Expert commands for DeepWork CLI."""
+
+from pathlib import Path
+
+import click
+from rich.console import Console
+
+from deepwork.core.experts_parser import (
+ ExpertParseError,
+ format_learnings_markdown,
+ format_topics_markdown,
+ parse_expert_definition,
+)
+
+console = Console()
+
+
+class ExpertNotFoundError(Exception):
+ """Exception raised when an expert is not found."""
+
+ pass
+
+
+def _find_expert_dir(expert_name: str, project_path: Path) -> Path:
+ """
+ Find the expert directory for a given expert name.
+
+ The expert name uses dashes (e.g., "rails-activejob") but the folder
+ name may use underscores (e.g., "rails_activejob").
+
+ Args:
+ expert_name: Expert name (with dashes)
+ project_path: Project root directory
+
+ Returns:
+ Path to expert directory
+
+ Raises:
+ ExpertNotFoundError: If expert is not found
+ """
+ experts_dir = project_path / ".deepwork" / "experts"
+
+ if not experts_dir.exists():
+ raise ExpertNotFoundError(
+ f"No experts directory found at {experts_dir}.\n"
+ "Run 'deepwork install' to set up the experts system."
+ )
+
+ # Convert expert name back to possible folder names
+ # rails-activejob -> rails_activejob, rails-activejob
+ possible_names = [
+ expert_name.replace("-", "_"), # rails_activejob
+ expert_name, # rails-activejob (direct match)
+ ]
+
+ for folder_name in possible_names:
+ expert_dir = experts_dir / folder_name
+ if expert_dir.exists() and (expert_dir / "expert.yml").exists():
+ return expert_dir
+
+ # List available experts for helpful error message
+ available_experts = []
+ if experts_dir.exists():
+ for subdir in experts_dir.iterdir():
+ if subdir.is_dir() and (subdir / "expert.yml").exists():
+ # Convert folder name to expert name (underscores to dashes)
+ name = subdir.name.replace("_", "-")
+ available_experts.append(name)
+
+ if available_experts:
+ available_str = ", ".join(sorted(available_experts))
+ raise ExpertNotFoundError(
+ f"Expert '{expert_name}' not found.\nAvailable experts: {available_str}"
+ )
+ else:
+ raise ExpertNotFoundError(
+ f"Expert '{expert_name}' not found.\nNo experts have been defined yet."
+ )
+
+
+@click.command()
+@click.option(
+ "--expert",
+ "-e",
+ required=True,
+ help="Expert name (e.g., 'rails-activejob')",
+)
+@click.option(
+ "--path",
+ type=click.Path(exists=True, file_okay=False, path_type=Path),
+ default=".",
+ help="Path to project directory (default: current directory)",
+)
+def topics(expert: str, path: Path) -> None:
+ """
+ List topics for an expert.
+
+ Returns a Markdown list of topics with names, file paths as links,
+ and keywords, sorted by most-recently-updated.
+
+ Example:
+ deepwork topics --expert "rails-activejob"
+ """
+ try:
+ expert_dir = _find_expert_dir(expert, path)
+ expert_def = parse_expert_definition(expert_dir)
+ output = format_topics_markdown(expert_def)
+ # Print raw output (no Rich formatting) for use in $(command) embedding
+ print(output)
+ except ExpertNotFoundError as e:
+ console.print(f"[red]Error:[/red] {e}")
+ raise click.Abort() from e
+ except ExpertParseError as e:
+ console.print(f"[red]Error parsing expert:[/red] {e}")
+ raise click.Abort() from e
+
+
+@click.command()
+@click.option(
+ "--expert",
+ "-e",
+ required=True,
+ help="Expert name (e.g., 'rails-activejob')",
+)
+@click.option(
+ "--path",
+ type=click.Path(exists=True, file_okay=False, path_type=Path),
+ default=".",
+ help="Path to project directory (default: current directory)",
+)
+def learnings(expert: str, path: Path) -> None:
+ """
+ List learnings for an expert.
+
+ Returns a Markdown list of learnings with names, file paths as links,
+ and summarized results, sorted by most-recently-updated.
+
+ Example:
+ deepwork learnings --expert "rails-activejob"
+ """
+ try:
+ expert_dir = _find_expert_dir(expert, path)
+ expert_def = parse_expert_definition(expert_dir)
+ output = format_learnings_markdown(expert_def)
+ # Print raw output (no Rich formatting) for use in $(command) embedding
+ print(output)
+ except ExpertNotFoundError as e:
+ console.print(f"[red]Error:[/red] {e}")
+ raise click.Abort() from e
+ except ExpertParseError as e:
+ console.print(f"[red]Error parsing expert:[/red] {e}")
+ raise click.Abort() from e
diff --git a/src/deepwork/cli/install.py b/src/deepwork/cli/install.py
index 19bec4f8..5aa3d398 100644
--- a/src/deepwork/cli/install.py
+++ b/src/deepwork/cli/install.py
@@ -21,85 +21,89 @@ class InstallError(Exception):
pass
-def _inject_standard_job(job_name: str, jobs_dir: Path, project_path: Path) -> None:
+def _inject_standard_experts(experts_dir: Path, project_path: Path) -> int:
"""
- Inject a standard job definition into the project.
+ Inject standard expert definitions into the project.
+
+ Copies all experts from src/deepwork/standard/experts/ to .deepwork/experts/.
+ Experts now contain their own workflows as subdirectories.
Args:
- job_name: Name of the standard job to inject
- jobs_dir: Path to .deepwork/jobs directory
+ experts_dir: Path to .deepwork/experts directory
project_path: Path to project root (for relative path display)
+ Returns:
+ Number of experts installed
+
Raises:
InstallError: If injection fails
"""
- # Find the standard jobs directory
- standard_jobs_dir = Path(__file__).parent.parent / "standard_jobs" / job_name
-
- if not standard_jobs_dir.exists():
- raise InstallError(
- f"Standard job '{job_name}' not found at {standard_jobs_dir}. "
- "DeepWork installation may be corrupted."
- )
-
- # Target directory
- target_dir = jobs_dir / job_name
-
- # Copy the entire directory
- try:
- if target_dir.exists():
- # Remove existing if present (for reinstall/upgrade)
- shutil.rmtree(target_dir)
-
- shutil.copytree(standard_jobs_dir, target_dir)
- # Fix permissions - source may have restrictive permissions (e.g., read-only)
- fix_permissions(target_dir)
- console.print(
- f" [green]✓[/green] Installed {job_name} ({target_dir.relative_to(project_path)})"
- )
+ # Find the standard experts directory
+ standard_experts_dir = Path(__file__).parent.parent / "standard" / "experts"
+
+ if not standard_experts_dir.exists():
+ # No standard experts to install - this is OK
+ return 0
+
+ installed_count = 0
+
+ # Iterate through each expert in the standard experts directory
+ for expert_source_dir in standard_experts_dir.iterdir():
+ if not expert_source_dir.is_dir():
+ continue
+
+ # Check if this is a valid expert (has expert.yml)
+ if not (expert_source_dir / "expert.yml").exists():
+ continue
+
+ expert_name = expert_source_dir.name
+ target_dir = experts_dir / expert_name
+
+ try:
+ if target_dir.exists():
+ # Remove existing if present (for reinstall/upgrade)
+ shutil.rmtree(target_dir)
+
+ shutil.copytree(expert_source_dir, target_dir)
+ # Fix permissions - source may have restrictive permissions
+ fix_permissions(target_dir)
+
+ # Count workflows in this expert
+ workflows_dir = target_dir / "workflows"
+ workflow_count = 0
+ if workflows_dir.exists():
+ workflow_count = len(
+ [
+ d
+ for d in workflows_dir.iterdir()
+ if d.is_dir() and (d / "workflow.yml").exists()
+ ]
+ )
- # Copy any doc specs from the standard job to .deepwork/doc_specs/
- doc_specs_source = standard_jobs_dir / "doc_specs"
- doc_specs_target = project_path / ".deepwork" / "doc_specs"
- if doc_specs_source.exists():
- for doc_spec_file in doc_specs_source.glob("*.md"):
- target_doc_spec = doc_specs_target / doc_spec_file.name
- shutil.copy(doc_spec_file, target_doc_spec)
- # Fix permissions for copied doc spec
- fix_permissions(target_doc_spec)
+ if workflow_count > 0:
console.print(
- f" [green]✓[/green] Installed doc spec {doc_spec_file.name} ({target_doc_spec.relative_to(project_path)})"
+ f" [green]✓[/green] Installed expert: {expert_name} "
+ f"({workflow_count} workflow(s))"
)
- except Exception as e:
- raise InstallError(f"Failed to install {job_name}: {e}") from e
-
-
-def _inject_deepwork_jobs(jobs_dir: Path, project_path: Path) -> None:
- """
- Inject the deepwork_jobs job definition into the project.
+ else:
+ console.print(f" [green]✓[/green] Installed expert: {expert_name}")
+ installed_count += 1
- Args:
- jobs_dir: Path to .deepwork/jobs directory
- project_path: Path to project root (for relative path display)
+ # Copy any doc specs from the expert to .deepwork/doc_specs/
+ doc_specs_source = expert_source_dir / "doc_specs"
+ doc_specs_target = project_path / ".deepwork" / "doc_specs"
+ if doc_specs_source.exists():
+ for doc_spec_file in doc_specs_source.glob("*.md"):
+ target_doc_spec = doc_specs_target / doc_spec_file.name
+ shutil.copy(doc_spec_file, target_doc_spec)
+ # Fix permissions for copied doc spec
+ fix_permissions(target_doc_spec)
+ console.print(f" [green]✓[/green] Installed doc spec {doc_spec_file.name}")
- Raises:
- InstallError: If injection fails
- """
- _inject_standard_job("deepwork_jobs", jobs_dir, project_path)
+ except Exception as e:
+ raise InstallError(f"Failed to install expert {expert_name}: {e}") from e
-
-def _inject_deepwork_rules(jobs_dir: Path, project_path: Path) -> None:
- """
- Inject the deepwork_rules job definition into the project.
-
- Args:
- jobs_dir: Path to .deepwork/jobs directory
- project_path: Path to project root (for relative path display)
-
- Raises:
- InstallError: If injection fails
- """
- _inject_standard_job("deepwork_rules", jobs_dir, project_path)
+ return installed_count
def _create_deepwork_gitignore(deepwork_dir: Path) -> None:
@@ -151,7 +155,7 @@ def _create_tmp_directory(deepwork_dir: Path) -> None:
def _create_rules_directory(project_path: Path) -> bool:
"""
- Create the v2 rules directory structure with example templates.
+ Create the rules directory structure with example templates.
Creates .deepwork/rules/ with example rule files that users can customize.
Only creates the directory if it doesn't already exist.
@@ -170,8 +174,10 @@ def _create_rules_directory(project_path: Path) -> bool:
# Create the rules directory
ensure_dir(rules_dir)
- # Copy example rule templates from the deepwork_rules standard job
- example_rules_dir = Path(__file__).parent.parent / "standard_jobs" / "deepwork_rules" / "rules"
+ # Copy example rule templates from the deepwork_rules expert
+ example_rules_dir = (
+ Path(__file__).parent.parent / "standard" / "experts" / "deepwork_rules" / "rules"
+ )
if example_rules_dir.exists():
# Copy all .example files
@@ -224,7 +230,7 @@ def _create_rules_directory(project_path: Path) -> bool:
## Creating Rules Interactively
-Use `/deepwork_rules.define` to create new rules with guidance.
+Use `/deepwork-rules.define` to create new rules with guidance.
"""
readme_path = rules_dir / "README.md"
readme_path.write_text(readme_content)
@@ -344,17 +350,18 @@ def _install_deepwork(platform_name: str | None, project_path: Path) -> None:
# Step 3: Create .deepwork/ directory structure
console.print("[yellow]→[/yellow] Creating DeepWork directory structure...")
deepwork_dir = project_path / ".deepwork"
- jobs_dir = deepwork_dir / "jobs"
doc_specs_dir = deepwork_dir / "doc_specs"
+ experts_dir = deepwork_dir / "experts"
ensure_dir(deepwork_dir)
- ensure_dir(jobs_dir)
ensure_dir(doc_specs_dir)
+ ensure_dir(experts_dir)
console.print(f" [green]✓[/green] Created {deepwork_dir.relative_to(project_path)}/")
- # Step 3b: Inject standard jobs (core job definitions)
- console.print("[yellow]→[/yellow] Installing core job definitions...")
- _inject_deepwork_jobs(jobs_dir, project_path)
- _inject_deepwork_rules(jobs_dir, project_path)
+ # Step 3b: Inject standard experts (which include their workflows)
+ console.print("[yellow]→[/yellow] Installing standard experts...")
+ experts_count = _inject_standard_experts(experts_dir, project_path)
+ if experts_count == 0:
+ console.print(" [dim]•[/dim] No standard experts to install")
# Step 3c: Create .gitignore for temporary files
_create_deepwork_gitignore(deepwork_dir)
@@ -364,7 +371,7 @@ def _install_deepwork(platform_name: str | None, project_path: Path) -> None:
_create_tmp_directory(deepwork_dir)
console.print(" [green]✓[/green] Created .deepwork/tmp/.gitkeep")
- # Step 3e: Create rules directory with v2 templates
+ # Step 3e: Create rules directory with templates
if _create_rules_directory(project_path):
console.print(" [green]✓[/green] Created .deepwork/rules/ with example templates")
else:
@@ -383,15 +390,14 @@ def _install_deepwork(platform_name: str | None, project_path: Path) -> None:
# Initialize config structure
if "version" not in config_data:
- config_data["version"] = "0.1.0"
+ config_data["version"] = "0.2.0"
if "platforms" not in config_data:
config_data["platforms"] = []
# Add each platform if not already present
added_platforms: list[str] = []
- for i, platform in enumerate(platforms_to_add):
- adapter = detected_adapters[i]
+ for platform, adapter in zip(platforms_to_add, detected_adapters, strict=True):
if platform not in config_data["platforms"]:
config_data["platforms"].append(platform)
added_platforms.append(adapter.display_name)
@@ -423,5 +429,5 @@ def _install_deepwork(platform_name: str | None, project_path: Path) -> None:
console.print()
console.print("[bold]Next steps:[/bold]")
console.print(" 1. Start your agent CLI (ex. [cyan]claude[/cyan] or [cyan]gemini[/cyan])")
- console.print(" 2. Define your first job using the command [cyan]/deepwork_jobs[/cyan]")
+ console.print(" 2. Define your first workflow using [cyan]/experts.define[/cyan]")
console.print()
diff --git a/src/deepwork/cli/main.py b/src/deepwork/cli/main.py
index b503ea9a..b5541455 100644
--- a/src/deepwork/cli/main.py
+++ b/src/deepwork/cli/main.py
@@ -14,6 +14,7 @@ def cli() -> None:
# Import commands
+from deepwork.cli.experts import learnings, topics # noqa: E402
from deepwork.cli.hook import hook # noqa: E402
from deepwork.cli.install import install # noqa: E402
from deepwork.cli.rules import rules # noqa: E402
@@ -23,6 +24,8 @@ def cli() -> None:
cli.add_command(sync)
cli.add_command(hook)
cli.add_command(rules)
+cli.add_command(topics)
+cli.add_command(learnings)
if __name__ == "__main__":
diff --git a/src/deepwork/cli/sync.py b/src/deepwork/cli/sync.py
index 03c47a30..43812a4c 100644
--- a/src/deepwork/cli/sync.py
+++ b/src/deepwork/cli/sync.py
@@ -7,9 +7,13 @@
from rich.table import Table
from deepwork.core.adapters import AgentAdapter
-from deepwork.core.generator import SkillGenerator
-from deepwork.core.hooks_syncer import collect_job_hooks, sync_hooks_to_platform
-from deepwork.core.parser import parse_job_definition
+from deepwork.core.experts_generator import ExpertGenerator
+from deepwork.core.experts_parser import (
+ ExpertParseError,
+ discover_experts,
+ parse_expert_definition,
+)
+from deepwork.core.hooks_syncer import collect_expert_hooks, sync_hooks_to_platform
from deepwork.utils.fs import ensure_dir
from deepwork.utils.yaml_utils import load_yaml
@@ -33,8 +37,8 @@ def sync(path: Path) -> None:
"""
Sync DeepWork skills to all configured platforms.
- Regenerates all skills for job steps and core skills based on
- the current job definitions in .deepwork/jobs/.
+ Regenerates all skills for workflows and expert agents based on
+ the current expert definitions in .deepwork/experts/.
"""
try:
sync_skills(path)
@@ -80,43 +84,49 @@ def sync_skills(project_path: Path) -> None:
console.print("[bold cyan]Syncing DeepWork Skills[/bold cyan]\n")
- # Discover jobs
- jobs_dir = deepwork_dir / "jobs"
- if not jobs_dir.exists():
- job_dirs = []
- else:
- job_dirs = [d for d in jobs_dir.iterdir() if d.is_dir() and (d / "job.yml").exists()]
-
- console.print(f"[yellow]→[/yellow] Found {len(job_dirs)} job(s) to sync")
+ # Discover and parse experts (experts include their workflows)
+ experts_dir = deepwork_dir / "experts"
+ expert_dirs = discover_experts(experts_dir)
+ console.print(f"[yellow]→[/yellow] Found {len(expert_dirs)} expert(s) to sync")
- # Parse all jobs
- jobs = []
- failed_jobs: list[tuple[str, str]] = []
- for job_dir in job_dirs:
+ experts = []
+ failed_experts: list[tuple[str, str]] = []
+ total_workflows = 0
+ for expert_dir in expert_dirs:
try:
- job_def = parse_job_definition(job_dir)
- jobs.append(job_def)
- console.print(f" [green]✓[/green] Loaded {job_def.name} v{job_def.version}")
- except Exception as e:
- console.print(f" [red]✗[/red] Failed to load {job_dir.name}: {e}")
- failed_jobs.append((job_dir.name, str(e)))
+ expert_def = parse_expert_definition(expert_dir)
+ experts.append(expert_def)
+ workflow_count = len(expert_def.workflows)
+ total_workflows += workflow_count
+ if workflow_count > 0:
+ console.print(
+ f" [green]✓[/green] Loaded {expert_def.name} ({workflow_count} workflow(s))"
+ )
+ else:
+ console.print(f" [green]✓[/green] Loaded {expert_def.name}")
+ except ExpertParseError as e:
+ console.print(f" [red]✗[/red] Failed to load {expert_dir.name}: {e}")
+ failed_experts.append((expert_dir.name, str(e)))
- # Fail early if any jobs failed to parse
- if failed_jobs:
+ # Fail early if any experts failed to parse
+ if failed_experts:
console.print()
- console.print("[bold red]Sync aborted due to job parsing errors:[/bold red]")
- for job_name, error in failed_jobs:
- console.print(f" • {job_name}: {error}")
- raise SyncError(f"Failed to parse {len(failed_jobs)} job(s)")
+ console.print("[bold red]Sync aborted due to expert parsing errors:[/bold red]")
+ for expert_name, error in failed_experts:
+ console.print(f" • {expert_name}: {error}")
+ raise SyncError(f"Failed to parse {len(failed_experts)} expert(s)")
+
+ if total_workflows > 0:
+ console.print(f"[yellow]→[/yellow] Found {total_workflows} workflow(s) across all experts")
- # Collect hooks from all jobs
- job_hooks_list = collect_job_hooks(jobs_dir)
- if job_hooks_list:
- console.print(f"[yellow]→[/yellow] Found {len(job_hooks_list)} job(s) with hooks")
+ # Collect hooks from all experts (via their workflows)
+ expert_hooks_list = collect_expert_hooks(experts_dir)
+ if expert_hooks_list:
+ console.print(f"[yellow]→[/yellow] Found {len(expert_hooks_list)} expert(s) with hooks")
# Sync each platform
- generator = SkillGenerator()
- stats = {"platforms": 0, "skills": 0, "hooks": 0}
+ expert_generator = ExpertGenerator()
+ stats = {"platforms": 0, "skills": 0, "hooks": 0, "agents": 0}
for platform_name in platforms:
try:
@@ -134,26 +144,43 @@ def sync_skills(project_path: Path) -> None:
# Create skills directory
ensure_dir(skills_dir)
- # Generate skills for all jobs
+ # Generate expert agents (only for Claude currently - agents live in .claude/agents/)
all_skill_paths: list[Path] = []
- if jobs:
- console.print(" [dim]•[/dim] Generating skills...")
- for job in jobs:
+ if experts and adapter.name == "claude":
+ console.print(" [dim]•[/dim] Generating expert agents...")
+ for expert in experts:
try:
- job_paths = generator.generate_all_skills(
- job, adapter, platform_dir, project_root=project_path
+ agent_path = expert_generator.generate_expert_agent(
+ expert, adapter, platform_dir
+ )
+ stats["agents"] += 1
+ console.print(f" [green]✓[/green] {expert.name} ({agent_path.name})")
+ except Exception as e:
+ console.print(f" [red]✗[/red] Failed for {expert.name}: {e}")
+
+ # Generate workflow skills for all experts
+ if experts:
+ console.print(" [dim]•[/dim] Generating workflow skills...")
+ for expert in experts:
+ if not expert.workflows:
+ continue
+ try:
+ expert_skill_paths = expert_generator.generate_all_expert_skills(
+ expert, adapter, platform_dir, project_root=project_path
+ )
+ all_skill_paths.extend(expert_skill_paths)
+ stats["skills"] += len(expert_skill_paths)
+ console.print(
+ f" [green]✓[/green] {expert.name} ({len(expert_skill_paths)} skills)"
)
- all_skill_paths.extend(job_paths)
- stats["skills"] += len(job_paths)
- console.print(f" [green]✓[/green] {job.name} ({len(job_paths)} skills)")
except Exception as e:
- console.print(f" [red]✗[/red] Failed for {job.name}: {e}")
+ console.print(f" [red]✗[/red] Failed for {expert.name}: {e}")
# Sync hooks to platform settings
- if job_hooks_list:
+ if expert_hooks_list:
console.print(" [dim]•[/dim] Syncing hooks...")
try:
- hooks_count = sync_hooks_to_platform(project_path, adapter, job_hooks_list)
+ hooks_count = sync_hooks_to_platform(project_path, adapter, expert_hooks_list)
stats["hooks"] += hooks_count
if hooks_count > 0:
console.print(f" [green]✓[/green] Synced {hooks_count} hook(s)")
@@ -194,6 +221,8 @@ def sync_skills(project_path: Path) -> None:
table.add_column("Count", style="green")
table.add_row("Platforms synced", str(stats["platforms"]))
+ if stats["agents"] > 0:
+ table.add_row("Expert agents", str(stats["agents"]))
table.add_row("Total skills", str(stats["skills"]))
if stats["hooks"] > 0:
table.add_row("Hooks synced", str(stats["hooks"]))
diff --git a/src/deepwork/core/adapters.py b/src/deepwork/core/adapters.py
index 96b8ca00..4da149da 100644
--- a/src/deepwork/core/adapters.py
+++ b/src/deepwork/core/adapters.py
@@ -20,7 +20,7 @@ class SkillLifecycleHook(str, Enum):
These represent hook points in the AI agent's skill execution lifecycle.
Each adapter maps these generic names to platform-specific event names.
- The enum values are the generic names used in job.yml files.
+ The enum values are the generic names used in workflow.yml files.
"""
# Triggered after the agent finishes responding (before returning to user)
@@ -55,8 +55,8 @@ class AgentAdapter(ABC):
display_name: ClassVar[str]
config_dir: ClassVar[str]
skills_dir: ClassVar[str] = "skills"
- skill_template: ClassVar[str] = "skill-job-step.md.jinja"
- meta_skill_template: ClassVar[str] = "skill-job-meta.md.jinja"
+ skill_template: ClassVar[str] = "skill-workflow-step.md.jinja"
+ meta_skill_template: ClassVar[str] = "skill-workflow-meta.md.jinja"
# Mapping from generic SkillLifecycleHook to platform-specific event names.
# Subclasses should override this to provide platform-specific mappings.
@@ -563,8 +563,8 @@ class GeminiAdapter(AgentAdapter):
name = "gemini"
display_name = "Gemini CLI"
config_dir = ".gemini"
- skill_template = "skill-job-step.toml.jinja"
- meta_skill_template = "skill-job-meta.toml.jinja"
+ skill_template = "skill-workflow-step.toml.jinja"
+ meta_skill_template = "skill-workflow-meta.toml.jinja"
# Gemini CLI does NOT support skill-level hooks
# Hooks are global/project-level in settings.json, not per-skill
diff --git a/src/deepwork/core/experts_generator.py b/src/deepwork/core/experts_generator.py
new file mode 100644
index 00000000..8806dc3a
--- /dev/null
+++ b/src/deepwork/core/experts_generator.py
@@ -0,0 +1,695 @@
+"""Expert agent and workflow skill generator using Jinja2 templates."""
+
+from pathlib import Path
+from typing import Any
+
+from jinja2 import Environment, FileSystemLoader, TemplateNotFound
+
+from deepwork.core.adapters import AgentAdapter, SkillLifecycleHook
+from deepwork.core.doc_spec_parser import (
+ DocSpec,
+ DocSpecParseError,
+ parse_doc_spec_file,
+)
+from deepwork.core.experts_parser import (
+ ExpertDefinition,
+ WorkflowDefinition,
+ WorkflowStep,
+)
+from deepwork.schemas.workflow_schema import LIFECYCLE_HOOK_EVENTS
+from deepwork.utils.fs import safe_read, safe_write
+
+
+class ExpertGeneratorError(Exception):
+ """Exception raised for expert agent generation errors."""
+
+
+class WorkflowGeneratorError(Exception):
+ """Exception raised for workflow skill generation errors."""
+
+
+class ExpertGenerator:
+ """Generates agent files and workflow skills from expert definitions."""
+
+ # Template filenames
+ EXPERT_AGENT_TEMPLATE = "agent-expert.md.jinja"
+
+ def __init__(self, templates_dir: Path | str | None = None):
+ """
+ Initialize generator.
+
+ Args:
+ templates_dir: Path to templates directory
+ (defaults to package templates directory)
+ """
+ if templates_dir is None:
+ templates_dir = Path(__file__).parent.parent / "templates"
+
+ self.templates_dir = Path(templates_dir)
+
+ if not self.templates_dir.exists():
+ raise ExpertGeneratorError(f"Templates directory not found: {self.templates_dir}")
+
+ # Cache for loaded doc specs
+ self._doc_spec_cache: dict[Path, DocSpec] = {}
+
+ def _get_jinja_env(self, adapter: AgentAdapter) -> Environment:
+ """
+ Get Jinja2 environment for an adapter.
+
+ Args:
+ adapter: Agent adapter
+
+ Returns:
+ Jinja2 Environment
+ """
+ platform_templates_dir = adapter.get_template_dir(self.templates_dir)
+ if not platform_templates_dir.exists():
+ raise ExpertGeneratorError(
+ f"Templates for platform '{adapter.name}' not found at {platform_templates_dir}"
+ )
+
+ return Environment(
+ loader=FileSystemLoader(platform_templates_dir),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+
+ def _load_doc_spec(self, project_root: Path, doc_spec_path: str) -> DocSpec | None:
+ """
+ Load a doc spec by file path with caching.
+
+ Args:
+ project_root: Path to project root
+ doc_spec_path: Relative path to doc spec file
+
+ Returns:
+ DocSpec if file exists and parses, None otherwise
+ """
+ full_path = project_root / doc_spec_path
+ if full_path in self._doc_spec_cache:
+ return self._doc_spec_cache[full_path]
+
+ if not full_path.exists():
+ return None
+
+ try:
+ doc_spec = parse_doc_spec_file(full_path)
+ except DocSpecParseError:
+ return None
+
+ self._doc_spec_cache[full_path] = doc_spec
+ return doc_spec
+
+ # =========================================================================
+ # Expert agent generation
+ # =========================================================================
+
+ def _build_expert_context(self, expert: ExpertDefinition) -> dict[str, Any]:
+ """
+ Build template context for an expert.
+
+ Args:
+ expert: Expert definition
+
+ Returns:
+ Template context dictionary
+ """
+ # Build workflow info for the expert
+ workflows_info = []
+ for workflow in expert.workflows:
+ workflow_info = {
+ "name": workflow.name,
+ "summary": workflow.summary,
+ "step_count": len(workflow.steps),
+ "first_step": workflow.steps[0].id if workflow.steps else None,
+ }
+ workflows_info.append(workflow_info)
+
+ return {
+ "expert_name": expert.name,
+ "discovery_description": expert.discovery_description,
+ "full_expertise": expert.full_expertise,
+ "workflows": workflows_info,
+ "has_workflows": bool(expert.workflows),
+ }
+
+ def get_agent_filename(self, expert_name: str) -> str:
+ """
+ Get the filename for an expert agent.
+
+ Args:
+ expert_name: Name of the expert (e.g., "rails-activejob")
+
+ Returns:
+ Agent filename (e.g., "dwe_rails-activejob.md")
+ """
+ return f"dwe_{expert_name}.md"
+
+ def get_agent_name(self, expert_name: str) -> str:
+ """
+ Get the agent name field value for an expert.
+
+ Args:
+ expert_name: Name of the expert (e.g., "rails-activejob")
+
+ Returns:
+ Agent name (e.g., "rails-activejob")
+ """
+ return expert_name
+
+ def generate_expert_agent(
+ self,
+ expert: ExpertDefinition,
+ adapter: AgentAdapter,
+ output_dir: Path | str,
+ ) -> Path:
+ """
+ Generate an agent file from an expert definition.
+
+ Args:
+ expert: Expert definition
+ adapter: Agent adapter for the target platform
+ output_dir: Platform config directory (e.g., .claude/)
+
+ Returns:
+ Path to generated agent file
+
+ Raises:
+ ExpertGeneratorError: If generation fails
+ """
+ output_dir = Path(output_dir)
+
+ # Create agents subdirectory
+ agents_dir = output_dir / "agents"
+ agents_dir.mkdir(parents=True, exist_ok=True)
+
+ # Build context
+ context = self._build_expert_context(expert)
+
+ # Load and render template
+ env = self._get_jinja_env(adapter)
+ try:
+ template = env.get_template(self.EXPERT_AGENT_TEMPLATE)
+ except TemplateNotFound as e:
+ raise ExpertGeneratorError(
+ f"Expert agent template not found: {self.EXPERT_AGENT_TEMPLATE}"
+ ) from e
+
+ try:
+ rendered = template.render(**context)
+ except Exception as e:
+ raise ExpertGeneratorError(
+ f"Expert agent template rendering failed for '{expert.name}': {e}"
+ ) from e
+
+ # Write agent file
+ agent_filename = self.get_agent_filename(expert.name)
+ agent_path = agents_dir / agent_filename
+
+ try:
+ safe_write(agent_path, rendered)
+ except Exception as e:
+ raise ExpertGeneratorError(f"Failed to write agent file: {e}") from e
+
+ return agent_path
+
+ def generate_all_expert_agents(
+ self,
+ experts: list[ExpertDefinition],
+ adapter: AgentAdapter,
+ output_dir: Path | str,
+ ) -> list[Path]:
+ """
+ Generate agent files for all experts.
+
+ Args:
+ experts: List of expert definitions
+ adapter: Agent adapter for the target platform
+ output_dir: Platform config directory (e.g., .claude/)
+
+ Returns:
+ List of paths to generated agent files
+
+ Raises:
+ ExpertGeneratorError: If generation fails for any expert
+ """
+ agent_paths: list[Path] = []
+
+ for expert in experts:
+ agent_path = self.generate_expert_agent(expert, adapter, output_dir)
+ agent_paths.append(agent_path)
+
+ return agent_paths
+
+ # =========================================================================
+ # Workflow skill generation
+ # =========================================================================
+
+ def _build_hook_context(self, workflow: WorkflowDefinition, hook_action: Any) -> dict[str, Any]:
+ """
+ Build context for a single hook action.
+
+ Args:
+ workflow: Workflow definition
+ hook_action: WorkflowHookAction instance
+
+ Returns:
+ Hook context dictionary
+ """
+ hook_ctx: dict[str, Any] = {}
+ if hook_action.is_prompt():
+ hook_ctx["type"] = "prompt"
+ hook_ctx["content"] = hook_action.prompt
+ elif hook_action.is_prompt_file():
+ hook_ctx["type"] = "prompt_file"
+ hook_ctx["path"] = hook_action.prompt_file
+ # Read the prompt file content
+ prompt_file_path = workflow.workflow_dir / hook_action.prompt_file
+ prompt_content = safe_read(prompt_file_path)
+ if prompt_content is None:
+ raise WorkflowGeneratorError(f"Hook prompt file not found: {prompt_file_path}")
+ hook_ctx["content"] = prompt_content
+ elif hook_action.is_script():
+ hook_ctx["type"] = "script"
+ hook_ctx["path"] = hook_action.script
+ return hook_ctx
+
+ def _build_step_context(
+ self,
+ expert: ExpertDefinition,
+ workflow: WorkflowDefinition,
+ step: WorkflowStep,
+ adapter: AgentAdapter,
+ project_root: Path | None = None,
+ ) -> dict[str, Any]:
+ """
+ Build template context for a workflow step.
+
+ Args:
+ expert: Expert definition (parent)
+ workflow: Workflow definition
+ step: Step to generate context for
+ adapter: Agent adapter for platform-specific hook name mapping
+ project_root: Optional project root for loading doc specs
+
+ Returns:
+ Template context dictionary
+ """
+ # Read step instructions
+ instructions_file = workflow.workflow_dir / step.instructions_file
+ instructions_content = safe_read(instructions_file)
+ if instructions_content is None:
+ raise WorkflowGeneratorError(f"Step instructions file not found: {instructions_file}")
+
+ # Separate user inputs and file inputs
+ user_inputs = [
+ {"name": inp.name, "description": inp.description}
+ for inp in step.inputs
+ if inp.is_user_input()
+ ]
+ file_inputs = [
+ {"file": inp.file, "from_step": inp.from_step}
+ for inp in step.inputs
+ if inp.is_file_input()
+ ]
+
+ # Get step position in workflow
+ position = workflow.get_step_position(step.id)
+ next_step = workflow.get_next_step(step.id)
+ prev_step = workflow.get_prev_step(step.id)
+
+ # Build hooks context for all lifecycle events
+ hooks: dict[str, list[dict[str, Any]]] = {}
+ for event in LIFECYCLE_HOOK_EVENTS:
+ if event in step.hooks:
+ hook_enum = SkillLifecycleHook(event)
+ platform_event_name = adapter.get_platform_hook_name(hook_enum)
+ if platform_event_name:
+ hook_contexts = [
+ self._build_hook_context(workflow, hook_action)
+ for hook_action in step.hooks[event]
+ ]
+ if hook_contexts:
+ hooks[platform_event_name] = hook_contexts
+
+ # Claude Code: duplicate Stop hooks to SubagentStop
+ if "Stop" in hooks:
+ hooks["SubagentStop"] = hooks["Stop"]
+
+ # Build rich outputs context with doc spec information
+ outputs_context = []
+ for output in step.outputs:
+ output_ctx: dict[str, Any] = {
+ "file": output.file,
+ "has_doc_spec": output.has_doc_spec(),
+ }
+ if output.has_doc_spec() and output.doc_spec and project_root:
+ doc_spec = self._load_doc_spec(project_root, output.doc_spec)
+ if doc_spec:
+ output_ctx["doc_spec"] = {
+ "path": output.doc_spec,
+ "name": doc_spec.name,
+ "description": doc_spec.description,
+ "target_audience": doc_spec.target_audience,
+ "quality_criteria": [
+ {"name": c.name, "description": c.description}
+ for c in doc_spec.quality_criteria
+ ],
+ "example_document": doc_spec.example_document,
+ }
+ outputs_context.append(output_ctx)
+
+ return {
+ # Expert context
+ "expert_name": expert.name,
+ # Workflow context
+ "workflow_name": workflow.name,
+ "workflow_version": workflow.version,
+ "workflow_summary": workflow.summary,
+ "workflow_description": workflow.description,
+ "workflow_step_number": position[0] if position else 1,
+ "workflow_total_steps": position[1] if position else 1,
+ # Step context
+ "step_id": step.id,
+ "step_name": step.name,
+ "step_description": step.description,
+ "instructions_file": step.instructions_file,
+ "instructions_content": instructions_content,
+ "user_inputs": user_inputs,
+ "file_inputs": file_inputs,
+ "outputs": outputs_context,
+ "dependencies": step.dependencies,
+ "next_step": next_step,
+ "prev_step": prev_step,
+ "exposed": step.exposed,
+ "hooks": hooks,
+ "quality_criteria": step.quality_criteria,
+ # Use the expert's name as the agent - each expert becomes its own agent
+ "agent": expert.name,
+ }
+
+ def _build_workflow_meta_context(
+ self,
+ expert: ExpertDefinition,
+ workflow: WorkflowDefinition,
+ adapter: AgentAdapter,
+ ) -> dict[str, Any]:
+ """
+ Build template context for a workflow's meta-skill.
+
+ Args:
+ expert: Expert definition (parent)
+ workflow: Workflow definition
+ adapter: Agent adapter for platform-specific configuration
+
+ Returns:
+ Template context dictionary
+ """
+ # Build step info for the meta-skill
+ steps_info = []
+ for step in workflow.steps:
+ # Skill command is expert-name.step-id
+ skill_command = f"{expert.name}.{step.id}"
+
+ step_info = {
+ "id": step.id,
+ "name": step.name,
+ "description": step.description,
+ "command_name": skill_command,
+ "dependencies": step.dependencies,
+ "exposed": step.exposed,
+ }
+ steps_info.append(step_info)
+
+ # Build execution order info with concurrent step support
+ execution_entries_info = []
+ if workflow.execution_order:
+ for entry in workflow.execution_order:
+ entry_info: dict[str, Any] = {
+ "is_concurrent": entry.is_concurrent,
+ "step_ids": entry.step_ids,
+ }
+ if entry.is_concurrent:
+ concurrent_steps = []
+ for i, step_id in enumerate(entry.step_ids):
+ step = workflow.get_step(step_id)
+ concurrent_steps.append(
+ {
+ "id": step_id,
+ "name": step.name if step else step_id,
+ "description": step.description if step else "",
+ "task_number": i + 1,
+ }
+ )
+ entry_info["concurrent_steps"] = concurrent_steps
+ execution_entries_info.append(entry_info)
+ else:
+ # Default: each step is sequential
+ for step in workflow.steps:
+ execution_entries_info.append(
+ {
+ "is_concurrent": False,
+ "step_ids": [step.id],
+ }
+ )
+
+ first_step = workflow.steps[0].id if workflow.steps else None
+
+ return {
+ "expert_name": expert.name,
+ "workflow_name": workflow.name,
+ "workflow_version": workflow.version,
+ "workflow_summary": workflow.summary,
+ "workflow_description": workflow.description,
+ "total_steps": len(workflow.steps),
+ "steps": steps_info,
+ "execution_entries": execution_entries_info,
+ "first_step": first_step,
+ }
+
+ def get_workflow_meta_skill_filename(self, expert_name: str, workflow_name: str) -> str:
+ """
+ Get filename for a workflow meta-skill.
+
+ Args:
+ expert_name: Expert name (e.g., "deepwork-jobs")
+ workflow_name: Workflow name (e.g., "new_job")
+
+ Returns:
+ Skill filename (e.g., "deepwork-jobs.new_job/SKILL.md" for Claude)
+ """
+ # Use expert-name.workflow-name format
+ return f"{expert_name}.{workflow_name}/SKILL.md"
+
+ def get_step_skill_filename(self, expert_name: str, step_id: str, exposed: bool = False) -> str:
+ """
+ Get filename for a step skill.
+
+ Args:
+ expert_name: Expert name (e.g., "deepwork-jobs")
+ step_id: Step ID (e.g., "define")
+ exposed: Whether step is user-invocable
+
+ Returns:
+ Skill filename (e.g., "deepwork-jobs.define/SKILL.md" for Claude)
+ """
+ # 2-part naming: expert-name.step-id
+ return f"{expert_name}.{step_id}/SKILL.md"
+
+ def generate_workflow_meta_skill(
+ self,
+ expert: ExpertDefinition,
+ workflow: WorkflowDefinition,
+ adapter: AgentAdapter,
+ output_dir: Path | str,
+ ) -> Path:
+ """
+ Generate the meta-skill file for a workflow.
+
+ Args:
+ expert: Expert definition (parent)
+ workflow: Workflow definition
+ adapter: Agent adapter for the target platform
+ output_dir: Directory to write skill file to
+
+ Returns:
+ Path to generated meta-skill file
+
+ Raises:
+ WorkflowGeneratorError: If generation fails
+ """
+ output_dir = Path(output_dir)
+
+ # Create skills subdirectory
+ skills_dir = output_dir / adapter.skills_dir
+ skills_dir.mkdir(parents=True, exist_ok=True)
+
+ # Build context
+ context = self._build_workflow_meta_context(expert, workflow, adapter)
+
+ # Load and render template
+ env = self._get_jinja_env(adapter)
+ try:
+ template = env.get_template(adapter.meta_skill_template)
+ except TemplateNotFound as e:
+ raise WorkflowGeneratorError(
+ f"Workflow meta-skill template not found: {adapter.meta_skill_template}"
+ ) from e
+
+ try:
+ rendered = template.render(**context)
+ except Exception as e:
+ raise WorkflowGeneratorError(
+ f"Workflow meta-skill template rendering failed: {e}"
+ ) from e
+
+ # Write meta-skill file
+ skill_filename = self.get_workflow_meta_skill_filename(expert.name, workflow.name)
+ skill_path = skills_dir / skill_filename
+
+ # Ensure parent directories exist
+ skill_path.parent.mkdir(parents=True, exist_ok=True)
+
+ try:
+ safe_write(skill_path, rendered)
+ except Exception as e:
+ raise WorkflowGeneratorError(f"Failed to write meta-skill file: {e}") from e
+
+ return skill_path
+
+ def generate_workflow_step_skill(
+ self,
+ expert: ExpertDefinition,
+ workflow: WorkflowDefinition,
+ step: WorkflowStep,
+ adapter: AgentAdapter,
+ output_dir: Path | str,
+ project_root: Path | str | None = None,
+ ) -> Path:
+ """
+ Generate skill file for a single workflow step.
+
+ Args:
+ expert: Expert definition (parent)
+ workflow: Workflow definition
+ step: Step to generate skill for
+ adapter: Agent adapter for the target platform
+ output_dir: Directory to write skill file to
+ project_root: Optional project root for loading doc specs
+
+ Returns:
+ Path to generated skill file
+
+ Raises:
+ WorkflowGeneratorError: If generation fails
+ """
+ output_dir = Path(output_dir)
+ project_root_path = Path(project_root) if project_root else output_dir
+
+ # Create skills subdirectory
+ skills_dir = output_dir / adapter.skills_dir
+ skills_dir.mkdir(parents=True, exist_ok=True)
+
+ # Build context
+ context = self._build_step_context(expert, workflow, step, adapter, project_root_path)
+
+ # Load and render template
+ env = self._get_jinja_env(adapter)
+ try:
+ template = env.get_template(adapter.skill_template)
+ except TemplateNotFound as e:
+ raise WorkflowGeneratorError(
+ f"Workflow step template not found: {adapter.skill_template}"
+ ) from e
+
+ try:
+ rendered = template.render(**context)
+ except Exception as e:
+ raise WorkflowGeneratorError(f"Workflow step template rendering failed: {e}") from e
+
+ # Write skill file
+ skill_filename = self.get_step_skill_filename(expert.name, step.id, step.exposed)
+ skill_path = skills_dir / skill_filename
+
+ # Ensure parent directories exist
+ skill_path.parent.mkdir(parents=True, exist_ok=True)
+
+ try:
+ safe_write(skill_path, rendered)
+ except Exception as e:
+ raise WorkflowGeneratorError(f"Failed to write skill file: {e}") from e
+
+ return skill_path
+
+ def generate_all_workflow_skills(
+ self,
+ expert: ExpertDefinition,
+ workflow: WorkflowDefinition,
+ adapter: AgentAdapter,
+ output_dir: Path | str,
+ project_root: Path | str | None = None,
+ ) -> list[Path]:
+ """
+ Generate all skill files for a workflow: meta-skill and step skills.
+
+ Args:
+ expert: Expert definition (parent)
+ workflow: Workflow definition
+ adapter: Agent adapter for the target platform
+ output_dir: Directory to write skill files to
+ project_root: Optional project root for loading doc specs
+
+ Returns:
+ List of paths to generated skill files (meta-skill first, then steps)
+
+ Raises:
+ WorkflowGeneratorError: If generation fails
+ """
+ skill_paths: list[Path] = []
+ project_root_path = Path(project_root) if project_root else Path(output_dir)
+
+ # Generate meta-skill first
+ meta_skill_path = self.generate_workflow_meta_skill(expert, workflow, adapter, output_dir)
+ skill_paths.append(meta_skill_path)
+
+ # Generate step skills
+ for step in workflow.steps:
+ skill_path = self.generate_workflow_step_skill(
+ expert, workflow, step, adapter, output_dir, project_root_path
+ )
+ skill_paths.append(skill_path)
+
+ return skill_paths
+
+ def generate_all_expert_skills(
+ self,
+ expert: ExpertDefinition,
+ adapter: AgentAdapter,
+ output_dir: Path | str,
+ project_root: Path | str | None = None,
+ ) -> list[Path]:
+ """
+ Generate all skill files for all workflows in an expert.
+
+ Args:
+ expert: Expert definition
+ adapter: Agent adapter for the target platform
+ output_dir: Directory to write skill files to
+ project_root: Optional project root for loading doc specs
+
+ Returns:
+ List of paths to all generated skill files
+
+ Raises:
+ WorkflowGeneratorError: If generation fails
+ """
+ all_skill_paths: list[Path] = []
+
+ for workflow in expert.workflows:
+ workflow_paths = self.generate_all_workflow_skills(
+ expert, workflow, adapter, output_dir, project_root
+ )
+ all_skill_paths.extend(workflow_paths)
+
+ return all_skill_paths
diff --git a/src/deepwork/core/experts_parser.py b/src/deepwork/core/experts_parser.py
new file mode 100644
index 00000000..d7a298b2
--- /dev/null
+++ b/src/deepwork/core/experts_parser.py
@@ -0,0 +1,961 @@
+"""Expert and workflow definition parser."""
+
+import re
+from dataclasses import dataclass, field
+from datetime import date
+from pathlib import Path
+from typing import Any
+
+from deepwork.schemas.expert_schema import (
+ EXPERT_SCHEMA,
+ LEARNING_FRONTMATTER_SCHEMA,
+ TOPIC_FRONTMATTER_SCHEMA,
+)
+from deepwork.schemas.workflow_schema import LIFECYCLE_HOOK_EVENTS, WORKFLOW_SCHEMA
+from deepwork.utils.validation import ValidationError, validate_against_schema
+from deepwork.utils.yaml_utils import YAMLError, load_yaml, load_yaml_from_string
+
+
+class ExpertParseError(Exception):
+ """Exception raised for expert parsing errors."""
+
+
+class WorkflowParseError(Exception):
+ """Exception raised for workflow parsing errors."""
+
+
+def _folder_name_to_expert_name(folder_name: str) -> str:
+ """
+ Convert folder name to expert name.
+
+ Spaces and underscores become dashes.
+ Example: rails_activejob -> rails-activejob
+
+ Args:
+ folder_name: The folder name to convert
+
+ Returns:
+ The expert name
+ """
+ return folder_name.replace("_", "-").replace(" ", "-")
+
+
+def _parse_frontmatter_markdown(content: str, file_type: str) -> tuple[dict[str, Any], str]:
+ """
+ Parse frontmatter from markdown content.
+
+ Expects format:
+ ---
+ key: value
+ ---
+ markdown body
+
+ Args:
+ content: Full file content
+ file_type: Type of file for error messages (e.g., "topic", "learning")
+
+ Returns:
+ Tuple of (frontmatter dict, body content)
+
+ Raises:
+ ExpertParseError: If frontmatter is missing or invalid
+ """
+ # Match frontmatter pattern: starts with ---, ends with ---
+ pattern = r"^---[ \t]*\n(.*?)^---[ \t]*\n?(.*)"
+ match = re.match(pattern, content.strip(), re.DOTALL | re.MULTILINE)
+
+ if not match:
+ raise ExpertParseError(
+ f"{file_type.capitalize()} file must have YAML frontmatter (content between --- markers)"
+ )
+
+ frontmatter_yaml = match.group(1)
+ body = match.group(2).strip() if match.group(2) else ""
+
+ try:
+ frontmatter = load_yaml_from_string(frontmatter_yaml)
+ except YAMLError as e:
+ raise ExpertParseError(f"Failed to parse {file_type} frontmatter: {e}") from e
+
+ if frontmatter is None:
+ frontmatter = {}
+
+ return frontmatter, body
+
+
+def _normalize_frontmatter_for_validation(frontmatter: dict[str, Any]) -> dict[str, Any]:
+ """
+ Normalize frontmatter data for schema validation.
+
+ PyYAML auto-parses dates as datetime.date objects, but our schema
+ expects strings. This function converts them back to strings.
+
+ Args:
+ frontmatter: Parsed frontmatter data
+
+ Returns:
+ Normalized frontmatter with dates as strings
+ """
+ result = frontmatter.copy()
+ if "last_updated" in result and isinstance(result["last_updated"], date):
+ result["last_updated"] = result["last_updated"].isoformat()
+ return result
+
+
+def _parse_date(date_value: str | date | None) -> date | None:
+ """
+ Parse a date value.
+
+ Handles both string (YYYY-MM-DD) and datetime.date objects
+ (which PyYAML may auto-parse).
+
+ Args:
+ date_value: Date string, date object, or None
+
+ Returns:
+ Parsed date or None
+ """
+ if date_value is None:
+ return None
+ if isinstance(date_value, date):
+ return date_value
+ try:
+ return date.fromisoformat(date_value)
+ except (ValueError, TypeError):
+ return None
+
+
+# =============================================================================
+# Topic and Learning dataclasses
+# =============================================================================
+
+
+@dataclass
+class Topic:
+ """Represents a topic within an expert domain."""
+
+ name: str
+ keywords: list[str] = field(default_factory=list)
+ last_updated: date | None = None
+ body: str = ""
+ source_file: Path | None = None
+
+ @property
+ def relative_path(self) -> str | None:
+ """Get relative path for display (topics/filename.md)."""
+ if self.source_file is None:
+ return None
+ return f"topics/{self.source_file.name}"
+
+ @classmethod
+ def from_dict(
+ cls, data: dict[str, Any], body: str = "", source_file: Path | None = None
+ ) -> "Topic":
+ """Create Topic from dictionary."""
+ return cls(
+ name=data["name"],
+ keywords=data.get("keywords", []),
+ last_updated=_parse_date(data.get("last_updated")),
+ body=body,
+ source_file=source_file,
+ )
+
+
+@dataclass
+class Learning:
+ """Represents a learning within an expert domain."""
+
+ name: str
+ last_updated: date | None = None
+ summarized_result: str | None = None
+ body: str = ""
+ source_file: Path | None = None
+
+ @property
+ def relative_path(self) -> str | None:
+ """Get relative path for display (learnings/filename.md)."""
+ if self.source_file is None:
+ return None
+ return f"learnings/{self.source_file.name}"
+
+ @classmethod
+ def from_dict(
+ cls, data: dict[str, Any], body: str = "", source_file: Path | None = None
+ ) -> "Learning":
+ """Create Learning from dictionary."""
+ return cls(
+ name=data["name"],
+ last_updated=_parse_date(data.get("last_updated")),
+ summarized_result=data.get("summarized_result"),
+ body=body,
+ source_file=source_file,
+ )
+
+
+# =============================================================================
+# Workflow-related dataclasses
+# =============================================================================
+
+
+@dataclass
+class WorkflowStepInput:
+ """Represents a workflow step input (either user parameter or file from previous step)."""
+
+ # User parameter input
+ name: str | None = None
+ description: str | None = None
+
+ # File input from previous step
+ file: str | None = None
+ from_step: str | None = None
+
+ def is_user_input(self) -> bool:
+ """Check if this is a user parameter input."""
+ return self.name is not None and self.description is not None
+
+ def is_file_input(self) -> bool:
+ """Check if this is a file input from previous step."""
+ return self.file is not None and self.from_step is not None
+
+ @classmethod
+ def from_dict(cls, data: dict[str, Any]) -> "WorkflowStepInput":
+ """Create WorkflowStepInput from dictionary."""
+ return cls(
+ name=data.get("name"),
+ description=data.get("description"),
+ file=data.get("file"),
+ from_step=data.get("from_step"),
+ )
+
+
+@dataclass
+class WorkflowOutputSpec:
+ """Represents a workflow step output specification."""
+
+ file: str
+ doc_spec: str | None = None
+
+ def has_doc_spec(self) -> bool:
+ """Check if this output has a doc spec reference."""
+ return self.doc_spec is not None
+
+ @classmethod
+ def from_dict(cls, data: dict[str, Any] | str) -> "WorkflowOutputSpec":
+ """Create WorkflowOutputSpec from dictionary or string."""
+ if isinstance(data, str):
+ return cls(file=data)
+ return cls(
+ file=data["file"],
+ doc_spec=data.get("doc_spec"),
+ )
+
+
+@dataclass
+class WorkflowHookAction:
+ """Represents a workflow hook action configuration."""
+
+ # Inline prompt
+ prompt: str | None = None
+
+ # Prompt file reference (relative to workflow directory)
+ prompt_file: str | None = None
+
+ # Shell script reference (relative to workflow directory)
+ script: str | None = None
+
+ def is_prompt(self) -> bool:
+ """Check if this is an inline prompt hook."""
+ return self.prompt is not None
+
+ def is_prompt_file(self) -> bool:
+ """Check if this is a prompt file reference hook."""
+ return self.prompt_file is not None
+
+ def is_script(self) -> bool:
+ """Check if this is a shell script hook."""
+ return self.script is not None
+
+ @classmethod
+ def from_dict(cls, data: dict[str, Any]) -> "WorkflowHookAction":
+ """Create WorkflowHookAction from dictionary."""
+ return cls(
+ prompt=data.get("prompt"),
+ prompt_file=data.get("prompt_file"),
+ script=data.get("script"),
+ )
+
+
+@dataclass
+class WorkflowStep:
+ """Represents a single step in a workflow."""
+
+ id: str
+ name: str
+ description: str
+ instructions_file: str
+ inputs: list[WorkflowStepInput] = field(default_factory=list)
+ outputs: list[WorkflowOutputSpec] = field(default_factory=list)
+ dependencies: list[str] = field(default_factory=list)
+
+ # Hooks dict mapping lifecycle event names to HookAction lists
+ hooks: dict[str, list[WorkflowHookAction]] = field(default_factory=dict)
+
+ # If true, skill is user-invocable in menus
+ exposed: bool = False
+
+ # Declarative quality criteria
+ quality_criteria: list[str] = field(default_factory=list)
+
+ # Agent type for this step (e.g., "general-purpose")
+ agent: str | None = None
+
+ @classmethod
+ def from_dict(cls, data: dict[str, Any]) -> "WorkflowStep":
+ """Create WorkflowStep from dictionary."""
+ # Parse hooks structure
+ hooks: dict[str, list[WorkflowHookAction]] = {}
+ if "hooks" in data:
+ hooks_data = data["hooks"]
+ for event in LIFECYCLE_HOOK_EVENTS:
+ if event in hooks_data:
+ hooks[event] = [WorkflowHookAction.from_dict(h) for h in hooks_data[event]]
+
+ return cls(
+ id=data["id"],
+ name=data["name"],
+ description=data["description"],
+ instructions_file=data["instructions_file"],
+ inputs=[WorkflowStepInput.from_dict(inp) for inp in data.get("inputs", [])],
+ outputs=[WorkflowOutputSpec.from_dict(out) for out in data["outputs"]],
+ dependencies=data.get("dependencies", []),
+ hooks=hooks,
+ exposed=data.get("exposed", False),
+ quality_criteria=data.get("quality_criteria", []),
+ agent=data.get("agent"),
+ )
+
+
+@dataclass
+class ExecutionOrderEntry:
+ """Represents an entry in the execution order (single step or concurrent group)."""
+
+ step_ids: list[str]
+ is_concurrent: bool = False
+
+ @property
+ def first_step(self) -> str:
+ """Get the first step ID in this entry."""
+ return self.step_ids[0] if self.step_ids else ""
+
+ @classmethod
+ def from_data(cls, data: str | list[str]) -> "ExecutionOrderEntry":
+ """Create ExecutionOrderEntry from YAML data."""
+ if isinstance(data, str):
+ return cls(step_ids=[data], is_concurrent=False)
+ else:
+ return cls(step_ids=list(data), is_concurrent=len(data) > 1)
+
+
+@dataclass
+class WorkflowDefinition:
+ """Represents a complete workflow definition."""
+
+ name: str
+ version: str
+ summary: str
+ description: str | None
+ steps: list[WorkflowStep]
+ workflow_dir: Path
+ execution_order: list[ExecutionOrderEntry] = field(default_factory=list)
+
+ # Reference to parent expert (set after parsing)
+ expert_name: str | None = None
+
+ def get_step(self, step_id: str) -> WorkflowStep | None:
+ """Get step by ID."""
+ for step in self.steps:
+ if step.id == step_id:
+ return step
+ return None
+
+ def get_step_ids(self) -> list[str]:
+ """Get all step IDs in this workflow."""
+ return [step.id for step in self.steps]
+
+ def get_execution_order_steps(self) -> list[str]:
+ """Get flattened list of step IDs from execution order."""
+ if not self.execution_order:
+ # Default: steps in definition order
+ return self.get_step_ids()
+ result: list[str] = []
+ for entry in self.execution_order:
+ result.extend(entry.step_ids)
+ return result
+
+ def get_step_position(self, step_id: str) -> tuple[int, int] | None:
+ """
+ Get the position of a step within the workflow.
+
+ Returns:
+ Tuple of (1-based position, total steps), or None if not found
+ """
+ step_ids = self.get_execution_order_steps()
+ try:
+ index = step_ids.index(step_id)
+ return (index + 1, len(step_ids))
+ except ValueError:
+ return None
+
+ def get_next_step(self, step_id: str) -> str | None:
+ """Get the next step ID after the given step."""
+ step_ids = self.get_execution_order_steps()
+ try:
+ index = step_ids.index(step_id)
+ if index < len(step_ids) - 1:
+ return step_ids[index + 1]
+ except ValueError:
+ pass
+ return None
+
+ def get_prev_step(self, step_id: str) -> str | None:
+ """Get the previous step ID before the given step."""
+ step_ids = self.get_execution_order_steps()
+ try:
+ index = step_ids.index(step_id)
+ if index > 0:
+ return step_ids[index - 1]
+ except ValueError:
+ pass
+ return None
+
+ def validate_dependencies(self) -> None:
+ """Validate step dependencies."""
+ step_ids = {step.id for step in self.steps}
+
+ # Check all dependencies reference existing steps
+ for step in self.steps:
+ for dep_id in step.dependencies:
+ if dep_id not in step_ids:
+ raise WorkflowParseError(
+ f"Step '{step.id}' depends on non-existent step '{dep_id}'"
+ )
+
+ # Check for circular dependencies
+ visited = set()
+ rec_stack = set()
+
+ def has_cycle(step_id: str) -> bool:
+ visited.add(step_id)
+ rec_stack.add(step_id)
+
+ step = self.get_step(step_id)
+ if step:
+ for dep_id in step.dependencies:
+ if dep_id not in visited:
+ if has_cycle(dep_id):
+ return True
+ elif dep_id in rec_stack:
+ return True
+
+ rec_stack.remove(step_id)
+ return False
+
+ for step in self.steps:
+ if step.id not in visited:
+ if has_cycle(step.id):
+ raise WorkflowParseError(
+ f"Circular dependency detected involving step '{step.id}'"
+ )
+
+ def validate_file_inputs(self) -> None:
+ """Validate that file inputs reference valid steps and dependencies."""
+ for step in self.steps:
+ for inp in step.inputs:
+ if inp.is_file_input():
+ # Check that from_step exists
+ from_step = self.get_step(inp.from_step) # type: ignore
+ if from_step is None:
+ raise WorkflowParseError(
+ f"Step '{step.id}' references non-existent step "
+ f"'{inp.from_step}' in file input"
+ )
+
+ # Check that from_step is in dependencies
+ if inp.from_step not in step.dependencies:
+ raise WorkflowParseError(
+ f"Step '{step.id}' has file input from '{inp.from_step}' "
+ f"but '{inp.from_step}' is not in dependencies"
+ )
+
+ def validate_execution_order(self) -> None:
+ """Validate execution order references valid steps."""
+ if not self.execution_order:
+ return
+
+ step_ids = {step.id for step in self.steps}
+ seen_in_order = set()
+
+ for entry in self.execution_order:
+ for step_id in entry.step_ids:
+ if step_id not in step_ids:
+ raise WorkflowParseError(
+ f"Execution order references non-existent step '{step_id}'"
+ )
+ if step_id in seen_in_order:
+ raise WorkflowParseError(
+ f"Step '{step_id}' appears multiple times in execution_order"
+ )
+ seen_in_order.add(step_id)
+
+ @classmethod
+ def from_dict(cls, data: dict[str, Any], workflow_dir: Path) -> "WorkflowDefinition":
+ """Create WorkflowDefinition from dictionary."""
+ execution_order = [
+ ExecutionOrderEntry.from_data(entry) for entry in data.get("execution_order", [])
+ ]
+
+ return cls(
+ name=data["name"],
+ version=data["version"],
+ summary=data["summary"],
+ description=data.get("description"),
+ steps=[WorkflowStep.from_dict(step_data) for step_data in data["steps"]],
+ workflow_dir=workflow_dir,
+ execution_order=execution_order,
+ )
+
+
+# =============================================================================
+# Expert definition dataclass
+# =============================================================================
+
+
+@dataclass
+class ExpertDefinition:
+ """Represents a complete expert definition."""
+
+ name: str # Derived from folder name
+ discovery_description: str
+ full_expertise: str
+ expert_dir: Path
+ topics: list[Topic] = field(default_factory=list)
+ learnings: list[Learning] = field(default_factory=list)
+ workflows: list[WorkflowDefinition] = field(default_factory=list)
+
+ def get_topics_sorted(self) -> list[Topic]:
+ """
+ Get topics sorted by most-recently-updated.
+
+ Topics without last_updated are sorted last.
+ """
+ return sorted(
+ self.topics,
+ key=lambda t: (t.last_updated is not None, t.last_updated),
+ reverse=True,
+ )
+
+ def get_learnings_sorted(self) -> list[Learning]:
+ """
+ Get learnings sorted by most-recently-updated.
+
+ Learnings without last_updated are sorted last.
+ """
+ return sorted(
+ self.learnings,
+ key=lambda learning: (learning.last_updated is not None, learning.last_updated),
+ reverse=True,
+ )
+
+ def get_workflow(self, workflow_name: str) -> WorkflowDefinition | None:
+ """Get a workflow by name."""
+ for workflow in self.workflows:
+ if workflow.name == workflow_name:
+ return workflow
+ return None
+
+ def get_all_step_ids(self) -> set[str]:
+ """Get all step IDs across all workflows in this expert."""
+ step_ids: set[str] = set()
+ for workflow in self.workflows:
+ for step in workflow.steps:
+ step_ids.add(step.id)
+ return step_ids
+
+ def validate_unique_step_ids(self) -> None:
+ """Validate that step IDs are unique within the expert."""
+ seen_ids: dict[str, str] = {} # step_id -> workflow_name
+ for workflow in self.workflows:
+ for step in workflow.steps:
+ if step.id in seen_ids:
+ raise ExpertParseError(
+ f"Step ID '{step.id}' is duplicated in workflows "
+ f"'{seen_ids[step.id]}' and '{workflow.name}'"
+ )
+ seen_ids[step.id] = workflow.name
+
+ @classmethod
+ def from_dict(
+ cls,
+ data: dict[str, Any],
+ expert_dir: Path,
+ topics: list[Topic] | None = None,
+ learnings: list[Learning] | None = None,
+ workflows: list[WorkflowDefinition] | None = None,
+ ) -> "ExpertDefinition":
+ """
+ Create ExpertDefinition from dictionary.
+
+ Args:
+ data: Parsed YAML data from expert.yml
+ expert_dir: Directory containing expert definition
+ topics: List of parsed topics
+ learnings: List of parsed learnings
+ workflows: List of parsed workflows
+
+ Returns:
+ ExpertDefinition instance
+ """
+ name = _folder_name_to_expert_name(expert_dir.name)
+ return cls(
+ name=name,
+ discovery_description=data["discovery_description"].strip(),
+ full_expertise=data["full_expertise"].strip(),
+ expert_dir=expert_dir,
+ topics=topics or [],
+ learnings=learnings or [],
+ workflows=workflows or [],
+ )
+
+
+# =============================================================================
+# Parsing functions
+# =============================================================================
+
+
+def parse_topic_file(filepath: Path | str) -> Topic:
+ """
+ Parse a topic file.
+
+ Args:
+ filepath: Path to the topic file (markdown with YAML frontmatter)
+
+ Returns:
+ Parsed Topic
+
+ Raises:
+ ExpertParseError: If parsing fails or validation errors occur
+ """
+ filepath = Path(filepath)
+
+ if not filepath.exists():
+ raise ExpertParseError(f"Topic file does not exist: {filepath}")
+
+ if not filepath.is_file():
+ raise ExpertParseError(f"Topic path is not a file: {filepath}")
+
+ try:
+ content = filepath.read_text(encoding="utf-8")
+ except Exception as e:
+ raise ExpertParseError(f"Failed to read topic file: {e}") from e
+
+ frontmatter, body = _parse_frontmatter_markdown(content, "topic")
+
+ try:
+ # Normalize frontmatter for validation (converts date objects to strings)
+ normalized = _normalize_frontmatter_for_validation(frontmatter)
+ validate_against_schema(normalized, TOPIC_FRONTMATTER_SCHEMA)
+ except ValidationError as e:
+ raise ExpertParseError(f"Topic validation failed in {filepath.name}: {e}") from e
+
+ return Topic.from_dict(frontmatter, body, filepath)
+
+
+def parse_learning_file(filepath: Path | str) -> Learning:
+ """
+ Parse a learning file.
+
+ Args:
+ filepath: Path to the learning file (markdown with YAML frontmatter)
+
+ Returns:
+ Parsed Learning
+
+ Raises:
+ ExpertParseError: If parsing fails or validation errors occur
+ """
+ filepath = Path(filepath)
+
+ if not filepath.exists():
+ raise ExpertParseError(f"Learning file does not exist: {filepath}")
+
+ if not filepath.is_file():
+ raise ExpertParseError(f"Learning path is not a file: {filepath}")
+
+ try:
+ content = filepath.read_text(encoding="utf-8")
+ except Exception as e:
+ raise ExpertParseError(f"Failed to read learning file: {e}") from e
+
+ frontmatter, body = _parse_frontmatter_markdown(content, "learning")
+
+ try:
+ # Normalize frontmatter for validation (converts date objects to strings)
+ normalized = _normalize_frontmatter_for_validation(frontmatter)
+ validate_against_schema(normalized, LEARNING_FRONTMATTER_SCHEMA)
+ except ValidationError as e:
+ raise ExpertParseError(f"Learning validation failed in {filepath.name}: {e}") from e
+
+ return Learning.from_dict(frontmatter, body, filepath)
+
+
+def parse_workflow_definition(workflow_dir: Path | str) -> WorkflowDefinition:
+ """
+ Parse workflow definition from directory.
+
+ Args:
+ workflow_dir: Directory containing workflow.yml
+
+ Returns:
+ Parsed WorkflowDefinition
+
+ Raises:
+ WorkflowParseError: If parsing fails or validation errors occur
+ """
+ workflow_dir_path = Path(workflow_dir)
+
+ if not workflow_dir_path.exists():
+ raise WorkflowParseError(f"Workflow directory does not exist: {workflow_dir_path}")
+
+ if not workflow_dir_path.is_dir():
+ raise WorkflowParseError(f"Workflow path is not a directory: {workflow_dir_path}")
+
+ workflow_file = workflow_dir_path / "workflow.yml"
+ if not workflow_file.exists():
+ raise WorkflowParseError(f"workflow.yml not found in {workflow_dir_path}")
+
+ # Load YAML
+ try:
+ workflow_data = load_yaml(workflow_file)
+ except YAMLError as e:
+ raise WorkflowParseError(f"Failed to load workflow.yml: {e}") from e
+
+ if workflow_data is None:
+ raise WorkflowParseError("workflow.yml is empty")
+
+ # Validate against schema
+ try:
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
+ except ValidationError as e:
+ raise WorkflowParseError(f"Workflow definition validation failed: {e}") from e
+
+ # Parse into dataclass
+ workflow_def = WorkflowDefinition.from_dict(workflow_data, workflow_dir_path)
+
+ # Validate dependencies, file inputs, and execution order
+ workflow_def.validate_dependencies()
+ workflow_def.validate_file_inputs()
+ workflow_def.validate_execution_order()
+
+ return workflow_def
+
+
+def discover_expert_workflows(expert_dir: Path | str) -> list[Path]:
+ """
+ Discover all workflow directories in an expert.
+
+ A workflow directory is one that contains a workflow.yml file.
+
+ Args:
+ expert_dir: Directory containing the expert
+
+ Returns:
+ List of paths to workflow directories
+ """
+ expert_dir_path = Path(expert_dir)
+ workflows_dir = expert_dir_path / "workflows"
+
+ if not workflows_dir.exists():
+ return []
+
+ if not workflows_dir.is_dir():
+ return []
+
+ workflow_dirs: list[Path] = []
+ for subdir in workflows_dir.iterdir():
+ if subdir.is_dir() and (subdir / "workflow.yml").exists():
+ workflow_dirs.append(subdir)
+
+ return workflow_dirs
+
+
+def parse_expert_definition(expert_dir: Path | str) -> ExpertDefinition:
+ """
+ Parse expert definition from directory.
+
+ Args:
+ expert_dir: Directory containing expert.yml
+
+ Returns:
+ Parsed ExpertDefinition
+
+ Raises:
+ ExpertParseError: If parsing fails or validation errors occur
+ """
+ expert_dir_path = Path(expert_dir)
+
+ if not expert_dir_path.exists():
+ raise ExpertParseError(f"Expert directory does not exist: {expert_dir_path}")
+
+ if not expert_dir_path.is_dir():
+ raise ExpertParseError(f"Expert path is not a directory: {expert_dir_path}")
+
+ expert_file = expert_dir_path / "expert.yml"
+ if not expert_file.exists():
+ raise ExpertParseError(f"expert.yml not found in {expert_dir_path}")
+
+ # Load YAML
+ try:
+ expert_data = load_yaml(expert_file)
+ except YAMLError as e:
+ raise ExpertParseError(f"Failed to load expert.yml: {e}") from e
+
+ if expert_data is None:
+ raise ExpertParseError("expert.yml is empty")
+
+ # Validate against schema
+ try:
+ validate_against_schema(expert_data, EXPERT_SCHEMA)
+ except ValidationError as e:
+ raise ExpertParseError(f"Expert definition validation failed: {e}") from e
+
+ # Parse topics
+ topics: list[Topic] = []
+ topics_dir = expert_dir_path / "topics"
+ if topics_dir.exists() and topics_dir.is_dir():
+ for topic_file in topics_dir.glob("*.md"):
+ topic = parse_topic_file(topic_file)
+ topics.append(topic)
+
+ # Parse learnings
+ learnings: list[Learning] = []
+ learnings_dir = expert_dir_path / "learnings"
+ if learnings_dir.exists() and learnings_dir.is_dir():
+ for learning_file in learnings_dir.glob("*.md"):
+ learning = parse_learning_file(learning_file)
+ learnings.append(learning)
+
+ # Parse workflows
+ workflows: list[WorkflowDefinition] = []
+ workflow_dirs = discover_expert_workflows(expert_dir_path)
+ expert_name = _folder_name_to_expert_name(expert_dir_path.name)
+ for workflow_dir in workflow_dirs:
+ try:
+ workflow = parse_workflow_definition(workflow_dir)
+ workflow.expert_name = expert_name
+ workflows.append(workflow)
+ except WorkflowParseError as e:
+ # Re-raise with expert context
+ raise ExpertParseError(
+ f"Failed to parse workflow '{workflow_dir.name}' in expert '{expert_name}': {e}"
+ ) from e
+
+ expert = ExpertDefinition.from_dict(expert_data, expert_dir_path, topics, learnings, workflows)
+
+ # Validate unique step IDs across all workflows in this expert
+ expert.validate_unique_step_ids()
+
+ return expert
+
+
+def discover_experts(experts_dir: Path | str) -> list[Path]:
+ """
+ Discover all expert directories in a given directory.
+
+ An expert directory is one that contains an expert.yml file.
+
+ Args:
+ experts_dir: Directory containing expert subdirectories
+
+ Returns:
+ List of paths to expert directories
+ """
+ experts_dir_path = Path(experts_dir)
+
+ if not experts_dir_path.exists():
+ return []
+
+ if not experts_dir_path.is_dir():
+ return []
+
+ expert_dirs: list[Path] = []
+ for subdir in experts_dir_path.iterdir():
+ if subdir.is_dir() and (subdir / "expert.yml").exists():
+ expert_dirs.append(subdir)
+
+ return expert_dirs
+
+
+# =============================================================================
+# Formatting functions
+# =============================================================================
+
+
+def format_topics_markdown(expert: ExpertDefinition) -> str:
+ """
+ Format topics as a markdown list for CLI output.
+
+ Returns name and relative file path as a markdown link,
+ followed by keywords, sorted by most-recently-updated.
+
+ Args:
+ expert: Expert definition
+
+ Returns:
+ Markdown formatted list of topics
+ """
+ topics = expert.get_topics_sorted()
+ if not topics:
+ return "_No topics yet._"
+
+ lines = []
+ for topic in topics:
+ # Format: [Topic Name](topics/filename.md)
+ rel_path = topic.relative_path or "topics/unknown.md"
+ line = f"- [{topic.name}]({rel_path})"
+ if topic.keywords:
+ line += f"\n Keywords: {', '.join(topic.keywords)}"
+ lines.append(line)
+
+ return "\n".join(lines)
+
+
+def format_learnings_markdown(expert: ExpertDefinition) -> str:
+ """
+ Format learnings as a markdown list for CLI output.
+
+ Returns name and relative file path as a markdown link,
+ followed by the summarized result, sorted by most-recently-updated.
+
+ Args:
+ expert: Expert definition
+
+ Returns:
+ Markdown formatted list of learnings
+ """
+ learnings = expert.get_learnings_sorted()
+ if not learnings:
+ return "_No learnings yet._"
+
+ lines = []
+ for learning in learnings:
+ # Format: [Learning Name](learnings/filename.md)
+ rel_path = learning.relative_path or "learnings/unknown.md"
+ line = f"- [{learning.name}]({rel_path})"
+ if learning.summarized_result:
+ # Add indented summary
+ summary_lines = learning.summarized_result.strip().split("\n")
+ for summary_line in summary_lines:
+ line += f"\n {summary_line}"
+ lines.append(line)
+
+ return "\n".join(lines)
diff --git a/src/deepwork/core/generator.py b/src/deepwork/core/generator.py
deleted file mode 100644
index 05ba975c..00000000
--- a/src/deepwork/core/generator.py
+++ /dev/null
@@ -1,577 +0,0 @@
-"""Skill file generator using Jinja2 templates."""
-
-from pathlib import Path
-from typing import Any
-
-from jinja2 import Environment, FileSystemLoader, TemplateNotFound
-
-from deepwork.core.adapters import AgentAdapter, SkillLifecycleHook
-from deepwork.core.doc_spec_parser import (
- DocSpec,
- DocSpecParseError,
- parse_doc_spec_file,
-)
-from deepwork.core.parser import JobDefinition, Step
-from deepwork.schemas.job_schema import LIFECYCLE_HOOK_EVENTS
-from deepwork.utils.fs import safe_read, safe_write
-
-
-class GeneratorError(Exception):
- """Exception raised for skill generation errors."""
-
- pass
-
-
-class SkillGenerator:
- """Generates skill files from job definitions."""
-
- def __init__(self, templates_dir: Path | str | None = None):
- """
- Initialize generator.
-
- Args:
- templates_dir: Path to templates directory
- (defaults to package templates directory)
- """
- if templates_dir is None:
- # Use package templates directory
- templates_dir = Path(__file__).parent.parent / "templates"
-
- self.templates_dir = Path(templates_dir)
-
- if not self.templates_dir.exists():
- raise GeneratorError(f"Templates directory not found: {self.templates_dir}")
-
- # Cache for loaded doc specs (keyed by absolute file path)
- self._doc_spec_cache: dict[Path, DocSpec] = {}
-
- def _load_doc_spec(self, project_root: Path, doc_spec_path: str) -> DocSpec | None:
- """
- Load a doc spec by file path with caching.
-
- Args:
- project_root: Path to project root
- doc_spec_path: Relative path to doc spec file (e.g., ".deepwork/doc_specs/report.md")
-
- Returns:
- DocSpec if file exists and parses, None otherwise
- """
- full_path = project_root / doc_spec_path
- if full_path in self._doc_spec_cache:
- return self._doc_spec_cache[full_path]
-
- if not full_path.exists():
- return None
-
- try:
- doc_spec = parse_doc_spec_file(full_path)
- except DocSpecParseError:
- return None
-
- self._doc_spec_cache[full_path] = doc_spec
- return doc_spec
-
- def _get_jinja_env(self, adapter: AgentAdapter) -> Environment:
- """
- Get Jinja2 environment for an adapter.
-
- Args:
- adapter: Agent adapter
-
- Returns:
- Jinja2 Environment
- """
- platform_templates_dir = adapter.get_template_dir(self.templates_dir)
- if not platform_templates_dir.exists():
- raise GeneratorError(
- f"Templates for platform '{adapter.name}' not found at {platform_templates_dir}"
- )
-
- return Environment(
- loader=FileSystemLoader(platform_templates_dir),
- trim_blocks=True,
- lstrip_blocks=True,
- )
-
- def _is_standalone_step(self, job: JobDefinition, step: Step) -> bool:
- """
- Check if a step is standalone (not part of any workflow).
-
- A step is standalone if:
- - It's not listed in any workflow definition
- - OR (for backward compatibility) no workflows are defined and the step
- has no dependencies and no other steps depend on it
-
- Args:
- job: Job definition
- step: Step to check
-
- Returns:
- True if step is standalone
- """
- # If workflows are defined, use workflow membership
- if job.workflows:
- return job.get_workflow_for_step(step.id) is None
- else:
- # Backward compatibility: if no workflows defined, use dependency analysis
- # Step has dependencies - not standalone
- if step.dependencies:
- return False
-
- # Check if any other step depends on this step
- for other_step in job.steps:
- if step.id in other_step.dependencies:
- return False
-
- return True
-
- def _get_workflow_context(self, job: JobDefinition, step: Step) -> dict[str, Any]:
- """
- Build workflow context for a step.
-
- Args:
- job: Job definition
- step: Step to build context for
-
- Returns:
- Workflow context dictionary with workflow info, or empty dict if standalone
- """
- workflow = job.get_workflow_for_step(step.id)
- if not workflow:
- return {}
-
- position = job.get_step_position_in_workflow(step.id)
- return {
- "workflow_name": workflow.name,
- "workflow_summary": workflow.summary,
- "workflow_step_number": position[0] if position else 1,
- "workflow_total_steps": position[1] if position else 1,
- "workflow_next_step": job.get_next_step_in_workflow(step.id),
- "workflow_prev_step": job.get_prev_step_in_workflow(step.id),
- }
-
- def _build_hook_context(self, job: JobDefinition, hook_action: Any) -> dict[str, Any]:
- """
- Build context for a single hook action.
-
- Args:
- job: Job definition
- hook_action: HookAction instance
-
- Returns:
- Hook context dictionary
- """
- hook_ctx: dict[str, Any] = {}
- if hook_action.is_prompt():
- hook_ctx["type"] = "prompt"
- hook_ctx["content"] = hook_action.prompt
- elif hook_action.is_prompt_file():
- hook_ctx["type"] = "prompt_file"
- hook_ctx["path"] = hook_action.prompt_file
- # Read the prompt file content
- prompt_file_path = job.job_dir / hook_action.prompt_file
- prompt_content = safe_read(prompt_file_path)
- if prompt_content is None:
- raise GeneratorError(f"Hook prompt file not found: {prompt_file_path}")
- hook_ctx["content"] = prompt_content
- elif hook_action.is_script():
- hook_ctx["type"] = "script"
- hook_ctx["path"] = hook_action.script
- return hook_ctx
-
- def _build_step_context(
- self,
- job: JobDefinition,
- step: Step,
- step_index: int,
- adapter: AgentAdapter,
- project_root: Path | None = None,
- ) -> dict[str, Any]:
- """
- Build template context for a step.
-
- Args:
- job: Job definition
- step: Step to generate context for
- step_index: Index of step in job (0-based)
- adapter: Agent adapter for platform-specific hook name mapping
- project_root: Optional project root for loading doc specs
-
- Returns:
- Template context dictionary
- """
- # Read step instructions
- instructions_file = job.job_dir / step.instructions_file
- instructions_content = safe_read(instructions_file)
- if instructions_content is None:
- raise GeneratorError(f"Step instructions file not found: {instructions_file}")
-
- # Separate user inputs and file inputs
- user_inputs = [
- {"name": inp.name, "description": inp.description}
- for inp in step.inputs
- if inp.is_user_input()
- ]
- file_inputs = [
- {"file": inp.file, "from_step": inp.from_step}
- for inp in step.inputs
- if inp.is_file_input()
- ]
-
- # Check if this is a standalone step
- is_standalone = self._is_standalone_step(job, step)
-
- # Get workflow context (empty dict if standalone)
- workflow_ctx = self._get_workflow_context(job, step)
-
- # Determine next and previous steps based on workflow (if defined) or order
- next_step = None
- prev_step = None
- if not is_standalone:
- if workflow_ctx:
- # Use workflow-defined order
- next_step = workflow_ctx.get("workflow_next_step")
- prev_step = workflow_ctx.get("workflow_prev_step")
- else:
- # Backward compatibility: use step array order
- if step_index < len(job.steps) - 1:
- next_step = job.steps[step_index + 1].id
- if step_index > 0:
- prev_step = job.steps[step_index - 1].id
-
- # Build hooks context for all lifecycle events
- # Structure: {platform_event_name: [hook_contexts]}
- hooks: dict[str, list[dict[str, Any]]] = {}
- for event in LIFECYCLE_HOOK_EVENTS:
- if event in step.hooks:
- # Get platform-specific event name from adapter
- hook_enum = SkillLifecycleHook(event)
- platform_event_name = adapter.get_platform_hook_name(hook_enum)
- if platform_event_name:
- hook_contexts = [
- self._build_hook_context(job, hook_action)
- for hook_action in step.hooks[event]
- ]
- if hook_contexts:
- hooks[platform_event_name] = hook_contexts
-
- # Claude Code has separate Stop and SubagentStop events. When a Stop hook
- # is defined, also register it for SubagentStop so it triggers for both
- # the main agent and subagents.
- if "Stop" in hooks:
- hooks["SubagentStop"] = hooks["Stop"]
-
- # Backward compatibility: stop_hooks is after_agent hooks
- stop_hooks = hooks.get(
- adapter.get_platform_hook_name(SkillLifecycleHook.AFTER_AGENT) or "Stop", []
- )
-
- # Build rich outputs context with doc spec information
- outputs_context = []
- for output in step.outputs:
- output_ctx: dict[str, Any] = {
- "file": output.file,
- "has_doc_spec": output.has_doc_spec(),
- }
- if output.has_doc_spec() and output.doc_spec and project_root:
- doc_spec = self._load_doc_spec(project_root, output.doc_spec)
- if doc_spec:
- output_ctx["doc_spec"] = {
- "path": output.doc_spec,
- "name": doc_spec.name,
- "description": doc_spec.description,
- "target_audience": doc_spec.target_audience,
- "quality_criteria": [
- {"name": c.name, "description": c.description}
- for c in doc_spec.quality_criteria
- ],
- "example_document": doc_spec.example_document,
- }
- outputs_context.append(output_ctx)
-
- context = {
- "job_name": job.name,
- "job_version": job.version,
- "job_summary": job.summary,
- "job_description": job.description,
- "step_id": step.id,
- "step_name": step.name,
- "step_description": step.description,
- "step_number": step_index + 1, # 1-based for display
- "total_steps": len(job.steps),
- "instructions_file": step.instructions_file,
- "instructions_content": instructions_content,
- "user_inputs": user_inputs,
- "file_inputs": file_inputs,
- "outputs": outputs_context,
- "dependencies": step.dependencies,
- "next_step": next_step,
- "prev_step": prev_step,
- "is_standalone": is_standalone,
- "hooks": hooks, # New: all hooks by platform event name
- "stop_hooks": stop_hooks, # Backward compat: after_agent hooks only
- "quality_criteria": step.quality_criteria, # Declarative criteria with framing
- "agent": step.agent, # Agent type (e.g., "general-purpose") - triggers context: fork
- }
-
- # Add workflow context if step is part of a workflow
- context.update(workflow_ctx)
-
- return context
-
- def _build_meta_skill_context(
- self, job: JobDefinition, adapter: AgentAdapter
- ) -> dict[str, Any]:
- """
- Build template context for a job's meta-skill.
-
- Args:
- job: Job definition
- adapter: Agent adapter for platform-specific configuration
-
- Returns:
- Template context dictionary
- """
- # Build step info for the meta-skill
- steps_info = []
- for step in job.steps:
- skill_filename = adapter.get_step_skill_filename(job.name, step.id, step.exposed)
- # Extract just the skill name (without path and extension)
- # For Claude: job_name.step_id/SKILL.md -> job_name.step_id
- # For Gemini: job_name/step_id.toml -> job_name:step_id
- if adapter.name == "gemini":
- # Gemini uses colon for namespacing: job_name:step_id
- parts = skill_filename.replace(".toml", "").split("/")
- skill_name = ":".join(parts)
- else:
- # Claude uses directory/SKILL.md format, extract directory name
- # job_name.step_id/SKILL.md -> job_name.step_id
- skill_name = skill_filename.replace("/SKILL.md", "")
-
- # Get workflow info for step
- workflow = job.get_workflow_for_step(step.id)
- step_info = {
- "id": step.id,
- "name": step.name,
- "description": step.description,
- "command_name": skill_name,
- "dependencies": step.dependencies,
- "exposed": step.exposed,
- "is_standalone": self._is_standalone_step(job, step),
- }
- if workflow:
- step_info["workflow_name"] = workflow.name
-
- steps_info.append(step_info)
-
- # Build workflow info with concurrent step support
- workflows_info = []
- for workflow in job.workflows:
- # Build step entries with concurrency info
- step_entries_info = []
- for entry in workflow.step_entries:
- entry_info: dict[str, Any] = {
- "is_concurrent": entry.is_concurrent,
- "step_ids": entry.step_ids,
- }
- if entry.is_concurrent:
- # Add detailed step info for each concurrent step
- concurrent_steps = []
- for i, step_id in enumerate(entry.step_ids):
- step = job.get_step(step_id)
- concurrent_steps.append(
- {
- "id": step_id,
- "name": step.name if step else step_id,
- "description": step.description if step else "",
- "task_number": i + 1,
- }
- )
- entry_info["concurrent_steps"] = concurrent_steps
- step_entries_info.append(entry_info)
-
- workflows_info.append(
- {
- "name": workflow.name,
- "summary": workflow.summary,
- "steps": workflow.steps, # Flattened for backward compat
- "step_entries": step_entries_info, # New: with concurrency info
- "first_step": workflow.steps[0] if workflow.steps else None,
- }
- )
-
- # Identify standalone steps (not in any workflow)
- standalone_steps = [s for s in steps_info if s["is_standalone"]]
-
- return {
- "job_name": job.name,
- "job_version": job.version,
- "job_summary": job.summary,
- "job_description": job.description,
- "total_steps": len(job.steps),
- "steps": steps_info,
- "workflows": workflows_info,
- "standalone_steps": standalone_steps,
- "has_workflows": bool(job.workflows),
- }
-
- def generate_meta_skill(
- self,
- job: JobDefinition,
- adapter: AgentAdapter,
- output_dir: Path | str,
- ) -> Path:
- """
- Generate the meta-skill file for a job.
-
- The meta-skill is the primary user interface for a job, routing
- user intent to the appropriate step.
-
- Args:
- job: Job definition
- adapter: Agent adapter for the target platform
- output_dir: Directory to write skill file to
-
- Returns:
- Path to generated meta-skill file
-
- Raises:
- GeneratorError: If generation fails
- """
- output_dir = Path(output_dir)
-
- # Create skills subdirectory if needed
- skills_dir = output_dir / adapter.skills_dir
- skills_dir.mkdir(parents=True, exist_ok=True)
-
- # Build context
- context = self._build_meta_skill_context(job, adapter)
-
- # Load and render template
- env = self._get_jinja_env(adapter)
- try:
- template = env.get_template(adapter.meta_skill_template)
- except TemplateNotFound as e:
- raise GeneratorError(f"Meta-skill template not found: {e}") from e
-
- try:
- rendered = template.render(**context)
- except Exception as e:
- raise GeneratorError(f"Meta-skill template rendering failed: {e}") from e
-
- # Write meta-skill file
- skill_filename = adapter.get_meta_skill_filename(job.name)
- skill_path = skills_dir / skill_filename
-
- # Ensure parent directories exist (for Gemini's job_name/index.toml structure)
- skill_path.parent.mkdir(parents=True, exist_ok=True)
-
- try:
- safe_write(skill_path, rendered)
- except Exception as e:
- raise GeneratorError(f"Failed to write meta-skill file: {e}") from e
-
- return skill_path
-
- def generate_step_skill(
- self,
- job: JobDefinition,
- step: Step,
- adapter: AgentAdapter,
- output_dir: Path | str,
- project_root: Path | str | None = None,
- ) -> Path:
- """
- Generate skill file for a single step.
-
- Args:
- job: Job definition
- step: Step to generate skill for
- adapter: Agent adapter for the target platform
- output_dir: Directory to write skill file to
- project_root: Optional project root for loading doc specs (defaults to output_dir)
-
- Returns:
- Path to generated skill file
-
- Raises:
- GeneratorError: If generation fails
- """
- output_dir = Path(output_dir)
- project_root_path = Path(project_root) if project_root else output_dir
-
- # Create skills subdirectory if needed
- skills_dir = output_dir / adapter.skills_dir
- skills_dir.mkdir(parents=True, exist_ok=True)
-
- # Find step index
- try:
- step_index = next(i for i, s in enumerate(job.steps) if s.id == step.id)
- except StopIteration as e:
- raise GeneratorError(f"Step '{step.id}' not found in job '{job.name}'") from e
-
- # Build context (include exposed for template user-invocable setting)
- context = self._build_step_context(job, step, step_index, adapter, project_root_path)
- context["exposed"] = step.exposed
-
- # Load and render template
- env = self._get_jinja_env(adapter)
- try:
- template = env.get_template(adapter.skill_template)
- except TemplateNotFound as e:
- raise GeneratorError(f"Template not found: {e}") from e
-
- try:
- rendered = template.render(**context)
- except Exception as e:
- raise GeneratorError(f"Template rendering failed: {e}") from e
-
- # Write skill file
- skill_filename = adapter.get_step_skill_filename(job.name, step.id, step.exposed)
- skill_path = skills_dir / skill_filename
-
- # Ensure parent directories exist (for Gemini's job_name/step_id.toml structure)
- skill_path.parent.mkdir(parents=True, exist_ok=True)
-
- try:
- safe_write(skill_path, rendered)
- except Exception as e:
- raise GeneratorError(f"Failed to write skill file: {e}") from e
-
- return skill_path
-
- def generate_all_skills(
- self,
- job: JobDefinition,
- adapter: AgentAdapter,
- output_dir: Path | str,
- project_root: Path | str | None = None,
- ) -> list[Path]:
- """
- Generate all skill files for a job: meta-skill and step skills.
-
- Args:
- job: Job definition
- adapter: Agent adapter for the target platform
- output_dir: Directory to write skill files to
- project_root: Optional project root for loading doc specs (defaults to output_dir)
-
- Returns:
- List of paths to generated skill files (meta-skill first, then steps)
-
- Raises:
- GeneratorError: If generation fails
- """
- skill_paths = []
- project_root_path = Path(project_root) if project_root else Path(output_dir)
-
- # Generate meta-skill first (job-level entry point)
- meta_skill_path = self.generate_meta_skill(job, adapter, output_dir)
- skill_paths.append(meta_skill_path)
-
- # Generate step skills
- for step in job.steps:
- skill_path = self.generate_step_skill(job, step, adapter, output_dir, project_root_path)
- skill_paths.append(skill_path)
-
- return skill_paths
diff --git a/src/deepwork/core/hooks_syncer.py b/src/deepwork/core/hooks_syncer.py
index 35a01036..02e87659 100644
--- a/src/deepwork/core/hooks_syncer.py
+++ b/src/deepwork/core/hooks_syncer.py
@@ -1,4 +1,4 @@
-"""Hooks syncer for DeepWork - collects and syncs hooks from jobs to platform settings."""
+"""Hooks syncer for DeepWork - collects and syncs hooks from experts to platform settings."""
from dataclasses import dataclass, field
from pathlib import Path
@@ -19,8 +19,8 @@ class HooksSyncError(Exception):
class HookEntry:
"""Represents a single hook entry for a lifecycle event."""
- job_name: str # Job that provides this hook
- job_dir: Path # Full path to job directory
+ expert_name: str # Expert that provides this hook
+ expert_dir: Path # Full path to expert directory
script: str | None = None # Script filename (if script-based hook)
module: str | None = None # Python module (if module-based hook)
@@ -40,8 +40,8 @@ def get_command(self, project_path: Path) -> str:
hook_name = self.module.rsplit(".", 1)[-1]
return f"deepwork hook {hook_name}"
elif self.script:
- # Script path is: .deepwork/jobs/{job_name}/hooks/{script}
- script_path = self.job_dir / "hooks" / self.script
+ # Script path is: .deepwork/experts/{expert_name}/hooks/{script}
+ script_path = self.expert_dir / "hooks" / self.script
try:
return str(script_path.relative_to(project_path))
except ValueError:
@@ -60,25 +60,25 @@ class HookSpec:
@dataclass
-class JobHooks:
- """Hooks configuration for a job."""
+class ExpertHooks:
+ """Hooks configuration for an expert."""
- job_name: str
- job_dir: Path
+ expert_name: str
+ expert_dir: Path
hooks: dict[str, list[HookSpec]] = field(default_factory=dict) # event -> [HookSpec]
@classmethod
- def from_job_dir(cls, job_dir: Path) -> "JobHooks | None":
+ def from_expert_dir(cls, expert_dir: Path) -> "ExpertHooks | None":
"""
- Load hooks configuration from a job directory.
+ Load hooks configuration from an expert directory.
Args:
- job_dir: Path to job directory containing hooks/global_hooks.yml
+ expert_dir: Path to expert directory containing hooks/global_hooks.yml
Returns:
- JobHooks instance or None if no hooks defined
+ ExpertHooks instance or None if no hooks defined
"""
- hooks_file = job_dir / "hooks" / "global_hooks.yml"
+ hooks_file = expert_dir / "hooks" / "global_hooks.yml"
if not hooks_file.exists():
return None
@@ -113,46 +113,50 @@ def from_job_dir(cls, job_dir: Path) -> "JobHooks | None":
return None
return cls(
- job_name=job_dir.name,
- job_dir=job_dir,
+ expert_name=expert_dir.name,
+ expert_dir=expert_dir,
hooks=hooks,
)
-def collect_job_hooks(jobs_dir: Path) -> list[JobHooks]:
+def collect_expert_hooks(experts_dir: Path) -> list[ExpertHooks]:
"""
- Collect hooks from all jobs in the jobs directory.
+ Collect hooks from all experts in the experts directory.
Args:
- jobs_dir: Path to .deepwork/jobs directory
+ experts_dir: Path to .deepwork/experts directory
Returns:
- List of JobHooks for all jobs with hooks defined
+ List of ExpertHooks for all experts with hooks defined
"""
- if not jobs_dir.exists():
+ if not experts_dir.exists():
return []
- job_hooks_list = []
- for job_dir in jobs_dir.iterdir():
- if not job_dir.is_dir():
+ expert_hooks_list = []
+ for expert_dir in experts_dir.iterdir():
+ if not expert_dir.is_dir():
continue
- job_hooks = JobHooks.from_job_dir(job_dir)
- if job_hooks:
- job_hooks_list.append(job_hooks)
+ # Check if this is a valid expert (has expert.yml)
+ if not (expert_dir / "expert.yml").exists():
+ continue
+
+ expert_hooks = ExpertHooks.from_expert_dir(expert_dir)
+ if expert_hooks:
+ expert_hooks_list.append(expert_hooks)
- return job_hooks_list
+ return expert_hooks_list
def merge_hooks_for_platform(
- job_hooks_list: list[JobHooks],
+ expert_hooks_list: list[ExpertHooks],
project_path: Path,
) -> dict[str, list[dict[str, Any]]]:
"""
- Merge hooks from multiple jobs into a single configuration.
+ Merge hooks from multiple experts into a single configuration.
Args:
- job_hooks_list: List of JobHooks from different jobs
+ expert_hooks_list: List of ExpertHooks from different experts
project_path: Path to project root for relative path calculation
Returns:
@@ -160,15 +164,15 @@ def merge_hooks_for_platform(
"""
merged: dict[str, list[dict[str, Any]]] = {}
- for job_hooks in job_hooks_list:
- for event, hook_specs in job_hooks.hooks.items():
+ for expert_hooks in expert_hooks_list:
+ for event, hook_specs in expert_hooks.hooks.items():
if event not in merged:
merged[event] = []
for spec in hook_specs:
entry = HookEntry(
- job_name=job_hooks.job_name,
- job_dir=job_hooks.job_dir,
+ expert_name=expert_hooks.expert_name,
+ expert_dir=expert_hooks.expert_dir,
script=spec.script,
module=spec.module,
)
@@ -196,19 +200,20 @@ def merge_hooks_for_platform(
if "SubagentStop" not in merged:
merged["SubagentStop"] = []
for hook_config in merged["Stop"]:
- command = hook_config.get("hooks", [{}])[0].get("command", "")
- if not _hook_already_present(merged["SubagentStop"], command):
+ hooks_list = hook_config.get("hooks", [])
+ command = hooks_list[0].get("command", "") if hooks_list else ""
+ if command and not _hook_already_present(merged["SubagentStop"], command):
merged["SubagentStop"].append(hook_config)
return merged
-def _hook_already_present(hooks: list[dict[str, Any]], script_path: str) -> bool:
- """Check if a hook with the given script path is already in the list."""
+def _hook_already_present(hooks: list[dict[str, Any]], command: str) -> bool:
+ """Check if a hook with the given command is already in the list."""
for hook in hooks:
hook_list = hook.get("hooks", [])
for h in hook_list:
- if h.get("command") == script_path:
+ if h.get("command") == command:
return True
return False
@@ -216,15 +221,15 @@ def _hook_already_present(hooks: list[dict[str, Any]], script_path: str) -> bool
def sync_hooks_to_platform(
project_path: Path,
adapter: AgentAdapter,
- job_hooks_list: list[JobHooks],
+ expert_hooks_list: list[ExpertHooks],
) -> int:
"""
- Sync hooks from jobs to a specific platform's settings.
+ Sync hooks from experts to a specific platform's settings.
Args:
project_path: Path to project root
adapter: Agent adapter for the target platform
- job_hooks_list: List of JobHooks from jobs
+ expert_hooks_list: List of ExpertHooks from experts
Returns:
Number of hooks synced
@@ -232,8 +237,8 @@ def sync_hooks_to_platform(
Raises:
HooksSyncError: If sync fails
"""
- # Merge hooks from all jobs
- merged_hooks = merge_hooks_for_platform(job_hooks_list, project_path)
+ # Merge hooks from all experts
+ merged_hooks = merge_hooks_for_platform(expert_hooks_list, project_path)
if not merged_hooks:
return 0
diff --git a/src/deepwork/core/parser.py b/src/deepwork/core/parser.py
deleted file mode 100644
index 480ab6d4..00000000
--- a/src/deepwork/core/parser.py
+++ /dev/null
@@ -1,618 +0,0 @@
-"""Job definition parser."""
-
-from dataclasses import dataclass, field
-from pathlib import Path
-from typing import Any
-
-from deepwork.schemas.job_schema import JOB_SCHEMA, LIFECYCLE_HOOK_EVENTS
-from deepwork.utils.validation import ValidationError, validate_against_schema
-from deepwork.utils.yaml_utils import YAMLError, load_yaml
-
-
-class ParseError(Exception):
- """Exception raised for job parsing errors."""
-
- pass
-
-
-@dataclass
-class StepInput:
- """Represents a step input (either user parameter or file from previous step)."""
-
- # User parameter input
- name: str | None = None
- description: str | None = None
-
- # File input from previous step
- file: str | None = None
- from_step: str | None = None
-
- def is_user_input(self) -> bool:
- """Check if this is a user parameter input."""
- return self.name is not None and self.description is not None
-
- def is_file_input(self) -> bool:
- """Check if this is a file input from previous step."""
- return self.file is not None and self.from_step is not None
-
- @classmethod
- def from_dict(cls, data: dict[str, Any]) -> "StepInput":
- """Create StepInput from dictionary."""
- return cls(
- name=data.get("name"),
- description=data.get("description"),
- file=data.get("file"),
- from_step=data.get("from_step"),
- )
-
-
-@dataclass
-class OutputSpec:
- """Represents a step output specification, optionally with doc spec reference."""
-
- file: str
- doc_spec: str | None = None
-
- def has_doc_spec(self) -> bool:
- """Check if this output has a doc spec reference."""
- return self.doc_spec is not None
-
- @classmethod
- def from_dict(cls, data: dict[str, Any] | str) -> "OutputSpec":
- """
- Create OutputSpec from dictionary or string.
-
- Supports both formats:
- - String: "output.md" -> OutputSpec(file="output.md")
- - Dict: {"file": "output.md", "doc_spec": ".deepwork/doc_specs/report.md"}
- """
- if isinstance(data, str):
- return cls(file=data)
- return cls(
- file=data["file"],
- doc_spec=data.get("doc_spec"),
- )
-
-
-@dataclass
-class HookAction:
- """Represents a hook action configuration.
-
- Hook actions define what happens when a lifecycle hook is triggered.
- Three types are supported:
- - prompt: Inline prompt text for validation/action
- - prompt_file: Path to a file containing the prompt
- - script: Path to a shell script for custom logic
- """
-
- # Inline prompt
- prompt: str | None = None
-
- # Prompt file reference (relative to job directory)
- prompt_file: str | None = None
-
- # Shell script reference (relative to job directory)
- script: str | None = None
-
- def is_prompt(self) -> bool:
- """Check if this is an inline prompt hook."""
- return self.prompt is not None
-
- def is_prompt_file(self) -> bool:
- """Check if this is a prompt file reference hook."""
- return self.prompt_file is not None
-
- def is_script(self) -> bool:
- """Check if this is a shell script hook."""
- return self.script is not None
-
- @classmethod
- def from_dict(cls, data: dict[str, Any]) -> "HookAction":
- """Create HookAction from dictionary."""
- return cls(
- prompt=data.get("prompt"),
- prompt_file=data.get("prompt_file"),
- script=data.get("script"),
- )
-
-
-# Backward compatibility alias
-StopHook = HookAction
-
-
-@dataclass
-class Step:
- """Represents a single step in a job."""
-
- id: str
- name: str
- description: str
- instructions_file: str
- inputs: list[StepInput] = field(default_factory=list)
- outputs: list[OutputSpec] = field(default_factory=list)
- dependencies: list[str] = field(default_factory=list)
-
- # New: hooks dict mapping lifecycle event names to HookAction lists
- # Event names: after_agent, before_tool, before_prompt
- hooks: dict[str, list[HookAction]] = field(default_factory=dict)
-
- # If true, skill is user-invocable in menus. Default: false (hidden from menus).
- exposed: bool = False
-
- # Declarative quality criteria rendered with standard evaluation framing
- quality_criteria: list[str] = field(default_factory=list)
-
- # Agent type for this step (e.g., "general-purpose"). When set, skill uses context: fork
- agent: str | None = None
-
- @property
- def stop_hooks(self) -> list[HookAction]:
- """
- Backward compatibility property for stop_hooks.
-
- Returns hooks for after_agent event.
- """
- return self.hooks.get("after_agent", [])
-
- @classmethod
- def from_dict(cls, data: dict[str, Any]) -> "Step":
- """Create Step from dictionary."""
- # Parse new hooks structure
- hooks: dict[str, list[HookAction]] = {}
- if "hooks" in data:
- hooks_data = data["hooks"]
- for event in LIFECYCLE_HOOK_EVENTS:
- if event in hooks_data:
- hooks[event] = [HookAction.from_dict(h) for h in hooks_data[event]]
-
- # Handle deprecated stop_hooks -> after_agent
- if "stop_hooks" in data and data["stop_hooks"]:
- # Merge with any existing after_agent hooks
- after_agent_hooks = hooks.get("after_agent", [])
- after_agent_hooks.extend([HookAction.from_dict(h) for h in data["stop_hooks"]])
- hooks["after_agent"] = after_agent_hooks
-
- return cls(
- id=data["id"],
- name=data["name"],
- description=data["description"],
- instructions_file=data["instructions_file"],
- inputs=[StepInput.from_dict(inp) for inp in data.get("inputs", [])],
- outputs=[OutputSpec.from_dict(out) for out in data["outputs"]],
- dependencies=data.get("dependencies", []),
- hooks=hooks,
- exposed=data.get("exposed", False),
- quality_criteria=data.get("quality_criteria", []),
- agent=data.get("agent"),
- )
-
-
-@dataclass
-class WorkflowStepEntry:
- """Represents a single entry in a workflow's step list.
-
- Each entry can be either:
- - A single step (sequential execution)
- - A list of steps (concurrent execution)
- """
-
- step_ids: list[str] # Single step has one ID, concurrent group has multiple
- is_concurrent: bool = False
-
- @property
- def first_step(self) -> str:
- """Get the first step ID in this entry."""
- return self.step_ids[0] if self.step_ids else ""
-
- def all_step_ids(self) -> list[str]:
- """Get all step IDs in this entry."""
- return self.step_ids
-
- @classmethod
- def from_data(cls, data: str | list[str]) -> "WorkflowStepEntry":
- """Create WorkflowStepEntry from YAML data (string or list)."""
- if isinstance(data, str):
- return cls(step_ids=[data], is_concurrent=False)
- else:
- return cls(step_ids=list(data), is_concurrent=True)
-
-
-@dataclass
-class Workflow:
- """Represents a named workflow grouping steps into a multi-step sequence."""
-
- name: str
- summary: str
- step_entries: list[WorkflowStepEntry] # List of step entries (sequential or concurrent)
-
- @property
- def steps(self) -> list[str]:
- """Get flattened list of all step IDs for backward compatibility."""
- result: list[str] = []
- for entry in self.step_entries:
- result.extend(entry.step_ids)
- return result
-
- def get_step_entry_for_step(self, step_id: str) -> WorkflowStepEntry | None:
- """Get the workflow step entry containing the given step ID."""
- for entry in self.step_entries:
- if step_id in entry.step_ids:
- return entry
- return None
-
- def get_entry_index_for_step(self, step_id: str) -> int | None:
- """Get the index of the entry containing the given step ID."""
- for i, entry in enumerate(self.step_entries):
- if step_id in entry.step_ids:
- return i
- return None
-
- @classmethod
- def from_dict(cls, data: dict[str, Any]) -> "Workflow":
- """Create Workflow from dictionary."""
- step_entries = [WorkflowStepEntry.from_data(s) for s in data["steps"]]
- return cls(
- name=data["name"],
- summary=data["summary"],
- step_entries=step_entries,
- )
-
-
-@dataclass
-class JobDefinition:
- """Represents a complete job definition."""
-
- name: str
- version: str
- summary: str
- description: str | None
- steps: list[Step]
- job_dir: Path
- workflows: list[Workflow] = field(default_factory=list)
-
- def get_step(self, step_id: str) -> Step | None:
- """
- Get step by ID.
-
- Args:
- step_id: Step ID to retrieve
-
- Returns:
- Step if found, None otherwise
- """
- for step in self.steps:
- if step.id == step_id:
- return step
- return None
-
- def validate_dependencies(self) -> None:
- """
- Validate step dependencies.
-
- Raises:
- ParseError: If dependencies are invalid (missing steps, circular deps)
- """
- step_ids = {step.id for step in self.steps}
-
- # Check all dependencies reference existing steps
- for step in self.steps:
- for dep_id in step.dependencies:
- if dep_id not in step_ids:
- raise ParseError(f"Step '{step.id}' depends on non-existent step '{dep_id}'")
-
- # Check for circular dependencies using topological sort
- visited = set()
- rec_stack = set()
-
- def has_cycle(step_id: str) -> bool:
- visited.add(step_id)
- rec_stack.add(step_id)
-
- step = self.get_step(step_id)
- if step:
- for dep_id in step.dependencies:
- if dep_id not in visited:
- if has_cycle(dep_id):
- return True
- elif dep_id in rec_stack:
- return True
-
- rec_stack.remove(step_id)
- return False
-
- for step in self.steps:
- if step.id not in visited:
- if has_cycle(step.id):
- raise ParseError(f"Circular dependency detected involving step '{step.id}'")
-
- def validate_file_inputs(self) -> None:
- """
- Validate that file inputs reference valid steps and dependencies.
-
- Raises:
- ParseError: If file inputs are invalid
- """
- for step in self.steps:
- for inp in step.inputs:
- if inp.is_file_input():
- # Check that from_step exists
- from_step = self.get_step(inp.from_step) # type: ignore
- if from_step is None:
- raise ParseError(
- f"Step '{step.id}' references non-existent step "
- f"'{inp.from_step}' in file input"
- )
-
- # Check that from_step is in dependencies
- if inp.from_step not in step.dependencies:
- raise ParseError(
- f"Step '{step.id}' has file input from '{inp.from_step}' "
- f"but '{inp.from_step}' is not in dependencies"
- )
-
- def validate_doc_spec_references(self, project_root: Path) -> None:
- """
- Validate that doc spec references in outputs point to existing files.
-
- Args:
- project_root: Path to the project root directory
-
- Raises:
- ParseError: If doc spec references are invalid
- """
- for step in self.steps:
- for output in step.outputs:
- if output.has_doc_spec():
- doc_spec_file = project_root / output.doc_spec
- if not doc_spec_file.exists():
- raise ParseError(
- f"Step '{step.id}' references non-existent doc spec "
- f"'{output.doc_spec}'. Expected file at {doc_spec_file}"
- )
-
- def get_doc_spec_references(self) -> list[str]:
- """
- Get all unique doc spec file paths referenced in this job's outputs.
-
- Returns:
- List of doc spec file paths (e.g., ".deepwork/doc_specs/report.md")
- """
- doc_spec_refs = set()
- for step in self.steps:
- for output in step.outputs:
- if output.has_doc_spec() and output.doc_spec:
- doc_spec_refs.add(output.doc_spec)
- return list(doc_spec_refs)
-
- def get_workflow_for_step(self, step_id: str) -> Workflow | None:
- """
- Get the workflow containing a step.
-
- Args:
- step_id: Step ID to look up
-
- Returns:
- Workflow containing the step, or None if step is standalone
- """
- for workflow in self.workflows:
- if step_id in workflow.steps:
- return workflow
- return None
-
- def get_next_step_in_workflow(self, step_id: str) -> str | None:
- """
- Get the next step in a workflow after the given step.
-
- Args:
- step_id: Current step ID
-
- Returns:
- Next step ID, or None if this is the last step or not in a workflow
- """
- workflow = self.get_workflow_for_step(step_id)
- if not workflow:
- return None
- try:
- index = workflow.steps.index(step_id)
- if index < len(workflow.steps) - 1:
- return workflow.steps[index + 1]
- except ValueError:
- pass
- return None
-
- def get_prev_step_in_workflow(self, step_id: str) -> str | None:
- """
- Get the previous step in a workflow before the given step.
-
- Args:
- step_id: Current step ID
-
- Returns:
- Previous step ID, or None if this is the first step or not in a workflow
- """
- workflow = self.get_workflow_for_step(step_id)
- if not workflow:
- return None
- try:
- index = workflow.steps.index(step_id)
- if index > 0:
- return workflow.steps[index - 1]
- except ValueError:
- pass
- return None
-
- def get_step_position_in_workflow(self, step_id: str) -> tuple[int, int] | None:
- """
- Get the position of a step within its workflow.
-
- Args:
- step_id: Step ID to look up
-
- Returns:
- Tuple of (1-based position, total steps in workflow), or None if standalone
- """
- workflow = self.get_workflow_for_step(step_id)
- if not workflow:
- return None
- try:
- index = workflow.steps.index(step_id)
- return (index + 1, len(workflow.steps))
- except ValueError:
- return None
-
- def get_step_entry_position_in_workflow(
- self, step_id: str
- ) -> tuple[int, int, WorkflowStepEntry] | None:
- """
- Get the entry-based position of a step within its workflow.
-
- For concurrent step groups, multiple steps share the same entry position.
-
- Args:
- step_id: Step ID to look up
-
- Returns:
- Tuple of (1-based entry position, total entries, WorkflowStepEntry),
- or None if standalone
- """
- workflow = self.get_workflow_for_step(step_id)
- if not workflow:
- return None
-
- entry_index = workflow.get_entry_index_for_step(step_id)
- if entry_index is None:
- return None
-
- entry = workflow.step_entries[entry_index]
- return (entry_index + 1, len(workflow.step_entries), entry)
-
- def get_concurrent_step_info(self, step_id: str) -> tuple[int, int] | None:
- """
- Get information about a step's position within a concurrent group.
-
- Args:
- step_id: Step ID to look up
-
- Returns:
- Tuple of (1-based position in group, total in group) if step is in
- a concurrent group, None if step is not in a concurrent group
- """
- workflow = self.get_workflow_for_step(step_id)
- if not workflow:
- return None
-
- entry = workflow.get_step_entry_for_step(step_id)
- if entry is None or not entry.is_concurrent:
- return None
-
- try:
- index = entry.step_ids.index(step_id)
- return (index + 1, len(entry.step_ids))
- except ValueError:
- return None
-
- def validate_workflows(self) -> None:
- """
- Validate workflow definitions.
-
- Raises:
- ParseError: If workflow references non-existent steps or has duplicates
- """
- step_ids = {step.id for step in self.steps}
- workflow_names = set()
-
- for workflow in self.workflows:
- # Check for duplicate workflow names
- if workflow.name in workflow_names:
- raise ParseError(f"Duplicate workflow name: '{workflow.name}'")
- workflow_names.add(workflow.name)
-
- # Check all step references exist
- for step_id in workflow.steps:
- if step_id not in step_ids:
- raise ParseError(
- f"Workflow '{workflow.name}' references non-existent step '{step_id}'"
- )
-
- # Check for duplicate steps within a workflow
- seen_steps = set()
- for step_id in workflow.steps:
- if step_id in seen_steps:
- raise ParseError(
- f"Workflow '{workflow.name}' contains duplicate step '{step_id}'"
- )
- seen_steps.add(step_id)
-
- @classmethod
- def from_dict(cls, data: dict[str, Any], job_dir: Path) -> "JobDefinition":
- """
- Create JobDefinition from dictionary.
-
- Args:
- data: Parsed YAML data
- job_dir: Directory containing job definition
-
- Returns:
- JobDefinition instance
- """
- workflows = [Workflow.from_dict(wf_data) for wf_data in data.get("workflows", [])]
- return cls(
- name=data["name"],
- version=data["version"],
- summary=data["summary"],
- description=data.get("description"),
- steps=[Step.from_dict(step_data) for step_data in data["steps"]],
- job_dir=job_dir,
- workflows=workflows,
- )
-
-
-def parse_job_definition(job_dir: Path | str) -> JobDefinition:
- """
- Parse job definition from directory.
-
- Args:
- job_dir: Directory containing job.yml
-
- Returns:
- Parsed JobDefinition
-
- Raises:
- ParseError: If parsing fails or validation errors occur
- """
- job_dir_path = Path(job_dir)
-
- if not job_dir_path.exists():
- raise ParseError(f"Job directory does not exist: {job_dir_path}")
-
- if not job_dir_path.is_dir():
- raise ParseError(f"Job path is not a directory: {job_dir_path}")
-
- job_file = job_dir_path / "job.yml"
- if not job_file.exists():
- raise ParseError(f"job.yml not found in {job_dir_path}")
-
- # Load YAML
- try:
- job_data = load_yaml(job_file)
- except YAMLError as e:
- raise ParseError(f"Failed to load job.yml: {e}") from e
-
- if job_data is None:
- raise ParseError("job.yml is empty")
-
- # Validate against schema
- try:
- validate_against_schema(job_data, JOB_SCHEMA)
- except ValidationError as e:
- raise ParseError(f"Job definition validation failed: {e}") from e
-
- # Parse into dataclass
- job_def = JobDefinition.from_dict(job_data, job_dir_path)
-
- # Validate dependencies, file inputs, and workflows
- job_def.validate_dependencies()
- job_def.validate_file_inputs()
- job_def.validate_workflows()
-
- return job_def
diff --git a/src/deepwork/schemas/expert_schema.py b/src/deepwork/schemas/expert_schema.py
new file mode 100644
index 00000000..df4fd379
--- /dev/null
+++ b/src/deepwork/schemas/expert_schema.py
@@ -0,0 +1,76 @@
+"""JSON Schema definitions for expert definitions."""
+
+from typing import Any
+
+# JSON Schema for expert.yml files
+EXPERT_SCHEMA: dict[str, Any] = {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "required": ["discovery_description", "full_expertise"],
+ "properties": {
+ "discovery_description": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Short description used to decide whether to invoke this expert. Keep concise and specific.",
+ },
+ "full_expertise": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Complete current knowledge of this domain (~5 pages max). This is included in the generated agent.",
+ },
+ },
+ "additionalProperties": False,
+}
+
+# JSON Schema for topic frontmatter
+TOPIC_FRONTMATTER_SCHEMA: dict[str, Any] = {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "required": ["name"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable topic name (e.g., 'Retry Handling')",
+ },
+ "keywords": {
+ "type": "array",
+ "description": "Topic-specific keywords (avoid broad terms like the expert domain)",
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ },
+ },
+ "last_updated": {
+ "type": "string",
+ "pattern": r"^\d{4}-\d{2}-\d{2}$",
+ "description": "Date stamp in YYYY-MM-DD format",
+ },
+ },
+ "additionalProperties": False,
+}
+
+# JSON Schema for learning frontmatter
+LEARNING_FRONTMATTER_SCHEMA: dict[str, Any] = {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "required": ["name"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable learning name (e.g., 'Job errors not going to Sentry')",
+ },
+ "last_updated": {
+ "type": "string",
+ "pattern": r"^\d{4}-\d{2}-\d{2}$",
+ "description": "Date stamp in YYYY-MM-DD format",
+ },
+ "summarized_result": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Brief summary of the key finding (1-3 sentences)",
+ },
+ },
+ "additionalProperties": False,
+}
diff --git a/src/deepwork/schemas/job_schema.py b/src/deepwork/schemas/workflow_schema.py
similarity index 80%
rename from src/deepwork/schemas/job_schema.py
rename to src/deepwork/schemas/workflow_schema.py
index e29b852c..b213182b 100644
--- a/src/deepwork/schemas/job_schema.py
+++ b/src/deepwork/schemas/workflow_schema.py
@@ -1,4 +1,8 @@
-"""JSON Schema definition for job definitions."""
+"""JSON Schema definitions for workflow definitions.
+
+Workflows are expert-owned multi-step sequences that live inside experts.
+Each expert can have zero or more workflows in their workflows/ subdirectory.
+"""
from typing import Any
@@ -27,7 +31,9 @@
"prompt_file": {
"type": "string",
"minLength": 1,
- "description": "Path to prompt file (relative to job directory)",
+ "pattern": "^[a-zA-Z0-9_][a-zA-Z0-9_./-]*$",
+ "not": {"pattern": r"\.\."},
+ "description": "Path to prompt file (relative to workflow directory). Cannot contain '..' for security.",
},
},
"additionalProperties": False,
@@ -38,7 +44,9 @@
"script": {
"type": "string",
"minLength": 1,
- "description": "Path to shell script (relative to job directory)",
+ "pattern": "^[a-zA-Z0-9_][a-zA-Z0-9_./-]*$",
+ "not": {"pattern": r"\.\."},
+ "description": "Path to shell script (relative to workflow directory). Cannot contain '..' for security.",
},
},
"additionalProperties": False,
@@ -53,51 +61,23 @@
}
# Schema for a concurrent step group (array of step IDs that can run in parallel)
-# minItems=1 allows single-item arrays to indicate a step with multiple parallel instances
-# (e.g., [fetch_campaign_data] means run this step for each campaign in parallel)
CONCURRENT_STEPS_SCHEMA: dict[str, Any] = {
"type": "array",
"minItems": 1,
- "description": "Array of step IDs that can be executed concurrently, or single step with multiple instances",
+ "description": "Array of step IDs that can be executed concurrently",
"items": STEP_ID_SCHEMA,
}
-# Schema for a workflow step entry (either single step or concurrent group)
-WORKFLOW_STEP_ENTRY_SCHEMA: dict[str, Any] = {
+# Schema for an execution order entry (either single step or concurrent group)
+EXECUTION_ORDER_ENTRY_SCHEMA: dict[str, Any] = {
"oneOf": [
STEP_ID_SCHEMA,
CONCURRENT_STEPS_SCHEMA,
],
}
-# Schema for a workflow definition
+# JSON Schema for workflow.yml files
WORKFLOW_SCHEMA: dict[str, Any] = {
- "type": "object",
- "required": ["name", "summary", "steps"],
- "properties": {
- "name": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9_]*$",
- "description": "Workflow name (lowercase letters, numbers, underscores)",
- },
- "summary": {
- "type": "string",
- "minLength": 1,
- "maxLength": 200,
- "description": "Brief one-line summary of what this workflow accomplishes",
- },
- "steps": {
- "type": "array",
- "minItems": 1,
- "description": "Ordered list of step entries. Each entry is either a step ID (string) or an array of step IDs for concurrent execution.",
- "items": WORKFLOW_STEP_ENTRY_SCHEMA,
- },
- },
- "additionalProperties": False,
-}
-
-# JSON Schema for job.yml files
-JOB_SCHEMA: dict[str, Any] = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["name", "version", "summary", "steps"],
@@ -105,7 +85,7 @@
"name": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$",
- "description": "Job name (lowercase letters, numbers, underscores, must start with letter)",
+ "description": "Workflow name, must match folder name (lowercase letters, numbers, underscores)",
},
"version": {
"type": "string",
@@ -116,43 +96,17 @@
"type": "string",
"minLength": 1,
"maxLength": 200,
- "description": "Brief one-line summary of what this job accomplishes",
+ "description": "Brief one-line summary of what this workflow accomplishes",
},
"description": {
"type": "string",
"minLength": 1,
- "description": "Detailed multi-line description of the job's purpose, process, and goals",
- },
- "workflows": {
- "type": "array",
- "description": "Named workflows that group steps into multi-step sequences",
- "items": WORKFLOW_SCHEMA,
- },
- "changelog": {
- "type": "array",
- "description": "Version history and changes to the job",
- "items": {
- "type": "object",
- "required": ["version", "changes"],
- "properties": {
- "version": {
- "type": "string",
- "pattern": r"^\d+\.\d+\.\d+$",
- "description": "Version number for this change",
- },
- "changes": {
- "type": "string",
- "minLength": 1,
- "description": "Description of changes made in this version",
- },
- },
- "additionalProperties": False,
- },
+ "description": "Detailed multi-line description of the workflow's purpose, process, and goals",
},
"steps": {
"type": "array",
"minItems": 1,
- "description": "List of steps in the job",
+ "description": "List of steps in the workflow",
"items": {
"type": "object",
"required": ["id", "name", "description", "instructions_file", "outputs"],
@@ -160,7 +114,7 @@
"id": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$",
- "description": "Step ID (unique within job)",
+ "description": "Step ID (unique within expert)",
},
"name": {
"type": "string",
@@ -175,7 +129,7 @@
"instructions_file": {
"type": "string",
"minLength": 1,
- "description": "Path to instructions file (relative to job directory)",
+ "description": "Path to instructions file (relative to workflow directory)",
},
"inputs": {
"type": "array",
@@ -222,7 +176,7 @@
{
"type": "string",
"minLength": 1,
- "description": "Simple output file path (backward compatible)",
+ "description": "Simple output file path",
},
{
"type": "object",
@@ -258,7 +212,7 @@
"properties": {
"after_agent": {
"type": "array",
- "description": "Hooks triggered after the agent finishes (quality validation)",
+ "description": "Hooks triggered after the agent finishes",
"items": HOOK_ACTION_SCHEMA,
},
"before_tool": {
@@ -274,20 +228,14 @@
},
"additionalProperties": False,
},
- # DEPRECATED: Use hooks.after_agent instead
- "stop_hooks": {
- "type": "array",
- "description": "DEPRECATED: Use hooks.after_agent instead. Stop hooks for quality validation loops.",
- "items": HOOK_ACTION_SCHEMA,
- },
"exposed": {
"type": "boolean",
- "description": "If true, skill is user-invocable in menus. Default: false (hidden from menus).",
+ "description": "If true, skill is user-invocable in menus. Default: false",
"default": False,
},
"quality_criteria": {
"type": "array",
- "description": "Declarative quality criteria. Rendered with standard evaluation framing.",
+ "description": "Declarative quality criteria for validation",
"items": {
"type": "string",
"minLength": 1,
@@ -295,8 +243,34 @@
},
"agent": {
"type": "string",
- "description": "Agent type for this step. When set, the skill uses context: fork and delegates to the specified agent (e.g., 'general-purpose').",
+ "description": "Agent type for this step (e.g., 'general-purpose'). Enables context: fork.",
+ "minLength": 1,
+ },
+ },
+ "additionalProperties": False,
+ },
+ },
+ "execution_order": {
+ "type": "array",
+ "description": "Explicit execution order with concurrent step support",
+ "items": EXECUTION_ORDER_ENTRY_SCHEMA,
+ },
+ "changelog": {
+ "type": "array",
+ "description": "Version history and changes to the workflow",
+ "items": {
+ "type": "object",
+ "required": ["version", "changes"],
+ "properties": {
+ "version": {
+ "type": "string",
+ "pattern": r"^\d+\.\d+\.\d+$",
+ "description": "Version number for this change",
+ },
+ "changes": {
+ "type": "string",
"minLength": 1,
+ "description": "Description of changes made in this version",
},
},
"additionalProperties": False,
diff --git a/src/deepwork/standard/experts/deepwork_rules/expert.yml b/src/deepwork/standard/experts/deepwork_rules/expert.yml
new file mode 100644
index 00000000..ea019c10
--- /dev/null
+++ b/src/deepwork/standard/experts/deepwork_rules/expert.yml
@@ -0,0 +1,89 @@
+discovery_description: |
+ DeepWork rules system - creating, organizing, and managing file-change rules that automatically trigger when specific files change during AI agent sessions.
+
+full_expertise: |
+ # DeepWork Rules Expert
+
+ Expert in creating and managing DeepWork rules - automated guardrails that trigger when specific files change during AI agent sessions.
+
+ ## What are Rules?
+
+ Rules are markdown files with YAML frontmatter that define:
+ - **Triggers**: File patterns that activate the rule when changed
+ - **Safety conditions**: File patterns that prevent the rule from firing when also changed
+ - **Instructions**: What the agent should do when the rule fires
+
+ ## Detection Modes
+
+ ### Trigger/Safety Mode (most common)
+ - Fires when trigger patterns match AND no safety patterns match
+ - Use for: "When X changes, check Y" rules
+ - Example: When config changes, verify install docs
+
+ ### Set Mode (bidirectional correspondence)
+ - Fires when files that should change together don't all change
+ - Use for: Source/test pairing, model/migration sync
+ - Example: `src/foo.py` and `tests/foo_test.py` should change together
+
+ ### Pair Mode (directional correspondence)
+ - Fires when a trigger file changes but expected files don't
+ - Changes to expected files alone do NOT trigger
+ - Use for: API code requires documentation updates (but docs can update independently)
+
+ ## Rule File Format
+
+ Rules live in `.deepwork/rules/` as markdown files:
+
+ ```markdown
+ ---
+ name: Rule Name
+ trigger: "pattern/**/*"
+ safety: "optional/pattern"
+ compare_to: base # optional
+ ---
+ Instructions for when this rule fires.
+ ```
+
+ ## Pattern Syntax
+
+ - `*` - Matches any characters within a single path segment
+ - `**` - Matches any characters across multiple path segments (recursive)
+ - `{name}` - Captures a single segment variable
+ - `{path}` - Captures multiple segments variable
+
+ ## Comparison Modes
+
+ - `base` (default) - Compare to merge-base with main/master
+ - `default_tip` - Compare to current tip of default branch
+ - `prompt` - Compare to state at start of each prompt
+
+ ## Action Types
+
+ Rules support two action types:
+
+ - **prompt** (default): Show instructions to the agent when the rule fires
+ - **command**: Execute a shell command automatically
+
+ Command action format:
+ ```markdown
+ ---
+ name: Auto Sync Dependencies
+ trigger: pyproject.toml
+ action: command
+ command: uv sync
+ ---
+ Dependencies will be automatically synced when pyproject.toml changes.
+ ```
+
+ Command actions are useful for:
+ - Auto-running `uv sync` when pyproject.toml changes
+ - Running linters when source files change
+ - Generating documentation when schemas change
+
+ ## When to Use Rules
+
+ Rules are most valuable for:
+ - Keeping documentation in sync with code
+ - Enforcing security reviews for sensitive changes
+ - Ensuring test coverage follows source changes
+ - Maintaining team guidelines automatically
diff --git a/src/deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh b/src/deepwork/standard/experts/deepwork_rules/hooks/capture_prompt_work_tree.sh
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh
rename to src/deepwork/standard/experts/deepwork_rules/hooks/capture_prompt_work_tree.sh
diff --git a/src/deepwork/standard_jobs/deepwork_rules/hooks/global_hooks.yml b/src/deepwork/standard/experts/deepwork_rules/hooks/global_hooks.yml
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/hooks/global_hooks.yml
rename to src/deepwork/standard/experts/deepwork_rules/hooks/global_hooks.yml
diff --git a/src/deepwork/standard_jobs/deepwork_rules/hooks/user_prompt_submit.sh b/src/deepwork/standard/experts/deepwork_rules/hooks/user_prompt_submit.sh
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/hooks/user_prompt_submit.sh
rename to src/deepwork/standard/experts/deepwork_rules/hooks/user_prompt_submit.sh
diff --git a/src/deepwork/standard_jobs/deepwork_rules/rules/api-documentation-sync.md.example b/src/deepwork/standard/experts/deepwork_rules/rules/api-documentation-sync.md.example
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/rules/api-documentation-sync.md.example
rename to src/deepwork/standard/experts/deepwork_rules/rules/api-documentation-sync.md.example
diff --git a/src/deepwork/standard_jobs/deepwork_rules/rules/readme-documentation.md.example b/src/deepwork/standard/experts/deepwork_rules/rules/readme-documentation.md.example
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/rules/readme-documentation.md.example
rename to src/deepwork/standard/experts/deepwork_rules/rules/readme-documentation.md.example
diff --git a/src/deepwork/standard_jobs/deepwork_rules/rules/security-review.md.example b/src/deepwork/standard/experts/deepwork_rules/rules/security-review.md.example
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/rules/security-review.md.example
rename to src/deepwork/standard/experts/deepwork_rules/rules/security-review.md.example
diff --git a/src/deepwork/standard_jobs/deepwork_rules/rules/skill-md-validation.md b/src/deepwork/standard/experts/deepwork_rules/rules/skill-md-validation.md
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/rules/skill-md-validation.md
rename to src/deepwork/standard/experts/deepwork_rules/rules/skill-md-validation.md
diff --git a/src/deepwork/standard_jobs/deepwork_rules/rules/source-test-pairing.md.example b/src/deepwork/standard/experts/deepwork_rules/rules/source-test-pairing.md.example
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_rules/rules/source-test-pairing.md.example
rename to src/deepwork/standard/experts/deepwork_rules/rules/source-test-pairing.md.example
diff --git a/src/deepwork/standard_jobs/deepwork_rules/steps/define.md b/src/deepwork/standard/experts/deepwork_rules/workflows/define/steps/define.md
similarity index 90%
rename from src/deepwork/standard_jobs/deepwork_rules/steps/define.md
rename to src/deepwork/standard/experts/deepwork_rules/workflows/define/steps/define.md
index 1e38a5e6..b9f2416b 100644
--- a/src/deepwork/standard_jobs/deepwork_rules/steps/define.md
+++ b/src/deepwork/standard/experts/deepwork_rules/workflows/define/steps/define.md
@@ -8,7 +8,7 @@ Create a new rule file in the `.deepwork/rules/` directory to enforce team guide
Guide the user through defining a new rule by asking structured questions. **Do not create the rule without first understanding what they want to enforce.**
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
+**Important**: Ask questions one at a time - present a question, wait for the user's response, then ask the next. Do not output all questions as a list.
### Step 1: Understand the Rule Purpose
@@ -141,6 +141,22 @@ Changed API: {trigger_files}
Update docs: {expected_files}
```
+**Format for Command Action (auto-run commands):**
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies will be automatically synced when pyproject.toml changes.
+```
+
+Command actions execute automatically instead of prompting the agent. Use for:
+- Dependency syncing (`uv sync`, `npm install`)
+- Linting/formatting (`ruff format`, `prettier`)
+- Code generation tasks
+
### Step 7: Verify the Rule
After creating the rule:
@@ -223,6 +239,18 @@ Changed API: {trigger_files}
Update: {expected_files}
```
+### Auto-Sync Dependencies (Command Action)
+`.deepwork/rules/auto-sync-deps.md`:
+```markdown
+---
+name: Auto Sync Dependencies
+trigger: pyproject.toml
+action: command
+command: uv sync
+---
+Dependencies are automatically synced when pyproject.toml changes.
+```
+
## Output Format
### .deepwork/rules/{rule-name}.md
diff --git a/src/deepwork/standard/experts/deepwork_rules/workflows/define/workflow.yml b/src/deepwork/standard/experts/deepwork_rules/workflows/define/workflow.yml
new file mode 100644
index 00000000..d77f3bcf
--- /dev/null
+++ b/src/deepwork/standard/experts/deepwork_rules/workflows/define/workflow.yml
@@ -0,0 +1,20 @@
+name: define
+version: "1.0.0"
+summary: "Create a new rule file that triggers when specified files change"
+
+description: |
+ Guide the user through defining a new DeepWork rule by asking structured questions
+ about what they want to enforce, then creating the rule file with proper YAML
+ frontmatter and markdown instructions.
+
+steps:
+ - id: define
+ name: "Define Rule"
+ description: "Create a new rule file that triggers when specified files change"
+ instructions_file: steps/define.md
+ inputs:
+ - name: rule_purpose
+ description: "What guideline or constraint should this rule enforce?"
+ outputs:
+ - ".deepwork/rules/{rule-name}.md"
+ exposed: true
diff --git a/src/deepwork/standard/experts/experts/expert.yml b/src/deepwork/standard/experts/experts/expert.yml
new file mode 100644
index 00000000..527cf68e
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/expert.yml
@@ -0,0 +1,340 @@
+discovery_description: |
+ DeepWork experts system - creating domain knowledge collections with topics, learnings,
+ and multi-step workflows. Covers expert.yml, workflow.yml schema, skill generation, and CLI commands.
+
+full_expertise: |
+ # DeepWork Experts System
+
+ You are an expert on the DeepWork experts system - the framework for building
+ auto-improving collections of domain knowledge with embedded workflows.
+
+ ## Core Concepts
+
+ **Experts** are structured knowledge repositories that grow smarter over time.
+ Each expert represents deep knowledge in a specific domain and consists of:
+
+ - **Core expertise**: Foundational knowledge captured in expert.yml
+ - **Topics**: Detailed documentation on specific subjects
+ - **Learnings**: Hard-fought insights from real experiences
+ - **Workflows**: Multi-step task sequences that can be invoked as skills
+
+ ## Expert Structure
+
+ Experts live in `.deepwork/experts/[folder-name]/`:
+
+ ```
+ .deepwork/experts/
+ └── rails_activejob/
+ ├── expert.yml
+ ├── topics/
+ │ └── retry_handling.md
+ ├── learnings/
+ │ └── job_errors_not_going_to_sentry.md
+ └── workflows/
+ └── debug_jobs/
+ ├── workflow.yml
+ └── steps/
+ └── analyze.md
+ ```
+
+ The **expert name** derives from the folder name with spaces/underscores becoming
+ dashes: `rails_activejob` → `rails-activejob`.
+
+ ## Writing Good expert.yml
+
+ The expert.yml has two key fields:
+
+ ### discovery_description
+ A concise description (1-3 sentences) that helps the system decide when to
+ invoke this expert. Be specific about the domain and capabilities.
+
+ Good: "Ruby on Rails ActiveJob - background job processing, retries, queues,
+ and error handling in Rails applications."
+
+ Bad: "Helps with Rails stuff."
+
+ ### full_expertise
+ The core knowledge payload (~5 pages max). Structure it as:
+
+ 1. **Identity statement**: "You are an expert on..."
+ 2. **Core concepts**: Key ideas and mental models
+ 3. **Common patterns**: Typical approaches and solutions
+ 4. **Pitfalls to avoid**: Known gotchas and mistakes
+ 5. **Decision frameworks**: How to choose between options
+
+ Write in second person ("You should...") as this becomes agent instructions.
+
+ ## Writing Good Topics
+
+ Topics are deep dives into specific subjects within the domain.
+
+ ### When to create a topic
+ - Subject needs more detail than fits in full_expertise
+ - You find yourself repeatedly explaining something
+ - A subject has enough nuance to warrant dedicated documentation
+
+ ### Topic file structure
+ ```markdown
+ ---
+ name: Retry Handling
+ keywords:
+ - retry
+ - exponential backoff
+ - dead letter queue
+ last_updated: 2025-01-15
+ ---
+
+ [Detailed content here]
+ ```
+
+ ### Keyword guidelines
+ - Use topic-specific terms only
+ - Avoid broad domain terms (don't use "Rails" in a Rails expert's topics)
+ - Include synonyms and related terms users might search for
+ - 3-7 keywords is typical
+
+ ## Writing Good Learnings
+
+ Learnings capture hard-fought insights from real experiences - like mini
+ retrospectives that prevent repeating mistakes.
+
+ ### When to create a learning
+ - You solved a non-obvious problem
+ - A debugging session revealed unexpected behavior
+ - You discovered something that contradicts common assumptions
+ - Future-you would benefit from this context
+
+ ### Learning file structure
+ ```markdown
+ ---
+ name: Job errors not going to Sentry
+ last_updated: 2025-01-20
+ summarized_result: |
+ Sentry changed their standard gem for hooking into jobs.
+ SolidQueue still worked but ActiveJobKubernetes did not.
+ ---
+
+ ## Context
+ What was happening and why it mattered...
+
+ ## Investigation
+ What you tried and what you discovered...
+
+ ## Resolution
+ How you fixed it and why that worked...
+
+ ## Key Takeaway
+ The generalizable insight for future reference...
+ ```
+
+ ## Workflows
+
+ **Workflows** are multi-step task sequences embedded within experts. Each workflow
+ becomes a set of slash commands that users can invoke.
+
+ ### Workflow Structure
+
+ Workflows live in `workflows/[workflow_name]/`:
+
+ ```
+ workflows/
+ └── new_job/
+ ├── workflow.yml
+ └── steps/
+ ├── define.md
+ └── implement.md
+ ```
+
+ ### The workflow.yml Schema
+
+ Required fields:
+ - `name`: lowercase with underscores, must match folder name
+ - `version`: semantic versioning X.Y.Z (e.g., `1.0.0`)
+ - `summary`: concise description under 200 characters
+ - `steps`: array of step definitions
+
+ Optional fields:
+ - `description`: detailed multi-line explanation
+ - `execution_order`: explicit step ordering with concurrent support
+ - `changelog`: version history
+
+ ### Step Definition Fields
+
+ Each step requires:
+ - `id`: unique identifier within the expert (across all workflows)
+ - `name`: human-readable name
+ - `description`: what this step accomplishes
+ - `instructions_file`: path to step markdown (e.g., `steps/define.md`)
+ - `outputs`: array of output files
+
+ Optional step fields:
+ - `inputs`: user parameters or file inputs from previous steps
+ - `dependencies`: array of step IDs this step requires
+ - `exposed`: boolean, if true skill appears in user menus (default: false)
+ - `quality_criteria`: array of criteria strings for validation
+ - `agent`: agent type for delegation (e.g., `general-purpose`)
+ - `hooks`: lifecycle hooks for validation
+
+ ### Input Types
+
+ **User inputs** - parameters gathered from the user:
+ ```yaml
+ inputs:
+ - name: market_segment
+ description: "Target market segment for research"
+ ```
+
+ **File inputs** - outputs from previous steps:
+ ```yaml
+ inputs:
+ - file: competitors_list.md
+ from_step: identify_competitors
+ ```
+
+ ### Concurrent Execution
+
+ Steps can run in parallel using execution_order:
+ ```yaml
+ execution_order:
+ - define
+ - [research_a, research_b] # concurrent
+ - synthesize
+ ```
+
+ ### Lifecycle Hooks
+
+ Hooks trigger at specific points:
+ - `after_agent`: runs after agent finishes (quality validation)
+ - `before_tool`: runs before tool use
+ - `before_prompt`: runs when user submits prompt
+
+ Hook action types:
+ ```yaml
+ hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+ ```
+
+ Note: Claude Code currently only supports script hooks.
+
+ ## Skill Generation
+
+ Running `deepwork sync` generates skills from expert definitions:
+
+ 1. Parses all experts in `.deepwork/experts/`
+ 2. For each workflow, generates step skills and a workflow meta-skill
+ 3. Writes to platform directories (e.g., `.claude/skills/`)
+
+ **Two types of skills are generated:**
+
+ 1. **Step skills** - One per step, contains the actual instructions
+ - Naming: `{expert-name}.{step-id}`
+ - Example: `experts.define`, `rails-activejob.analyze`
+ - Path: `.claude/skills/[expert-name].[step-id]/SKILL.md`
+
+ 2. **Workflow meta-skills** - One per workflow, orchestrates the steps
+ - Naming: `{expert-name}.{workflow-name}`
+ - Example: `experts.new_workflow`, `experts.review_pr`
+ - Path: `.claude/skills/[expert-name].[workflow-name]/SKILL.md`
+ - Purpose: Entry point that routes to the first step and auto-continues through the workflow
+
+ ## CLI Commands
+
+ **Install DeepWork**:
+ ```bash
+ deepwork install --platform claude
+ ```
+ Creates `.deepwork/` structure, copies standard experts, runs sync.
+
+ **Sync skills**:
+ ```bash
+ deepwork sync
+ ```
+ Regenerates all skills from expert definitions.
+
+ **List topics**:
+ ```bash
+ deepwork topics --expert "expert-name"
+ ```
+
+ **List learnings**:
+ ```bash
+ deepwork learnings --expert "expert-name"
+ ```
+
+ ## How Experts Become Agents
+
+ Running `deepwork sync` generates Claude agents in `.claude/agents/`:
+
+ - Filename: `dwe_[expert-name].md`
+ - Agent name: `[expert-name]`
+ - Body: full_expertise + dynamic topic/learning lists
+
+ The dynamic embedding ensures agents always access current topics and learnings:
+ ```
+ $(deepwork topics --expert "expert-name")
+ $(deepwork learnings --expert "expert-name")
+ ```
+
+ ## Standard Experts
+
+ DeepWork ships with standard experts that are auto-installed:
+
+ - `experts`: This expert - creating and managing experts with workflows
+ - `new_job` workflow: Define, review, and implement new workflows
+ - `learn` workflow: Extract learnings from workflow executions
+ - `review_pr` workflow: Expert-driven PR review
+
+ - `deepwork_rules`: Create file-change trigger rules
+ - `define` workflow: Interactive rule creation
+
+ ## Writing Step Instructions
+
+ Step instruction files should include:
+
+ 1. **Objective**: Clear statement of what this step accomplishes
+ 2. **Task**: Detailed process with numbered steps
+ 3. **Inputs section**: What to gather/read before starting
+ 4. **Output format**: Examples of expected outputs
+ 5. **Quality criteria**: How to verify the step is complete
+
+ Use the phrase "ask structured questions" when gathering user input -
+ this triggers proper tooling for interactive prompts.
+
+ ## Common Patterns
+
+ **Creating a new workflow**:
+ 1. Run `/experts.define` (or just `/experts`)
+ 2. Answer structured questions about your workflow
+ 3. Review generated workflow.yml
+ 4. Run `/experts.implement` to generate step files
+ 5. Run `deepwork sync` to create skills
+
+ **Adding a step to existing workflow**:
+ 1. Edit `.deepwork/experts/[expert]/workflows/[workflow]/workflow.yml`
+ 2. Add step definition with required fields
+ 3. Create instructions file in `steps/`
+ 4. Run `deepwork sync`
+
+ ## Evolution Strategy
+
+ Experts should evolve through use:
+
+ 1. **Start minimal**: Begin with core expertise, add topics/learnings as needed
+ 2. **Capture immediately**: Document learnings right after solving problems
+ 3. **Refine periodically**: Review and consolidate as patterns emerge
+ 4. **Prune actively**: Remove outdated content, merge redundant topics
+
+ ## Naming Conventions
+
+ ### Expert folders
+ - Use lowercase with underscores: `rails_activejob`, `social_marketing`
+ - Be specific enough to be useful: `react_hooks` not just `react`
+
+ ### Workflow folders
+ - Use lowercase with underscores: `new_job`, `review_pr`
+ - Name describes the workflow's purpose
+
+ ### Step IDs
+ - Must be unique within the expert (across all workflows)
+ - Use lowercase with underscores: `define`, `implement`
diff --git a/src/deepwork/standard/experts/experts/learnings/.gitkeep b/src/deepwork/standard/experts/experts/learnings/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/src/deepwork/standard/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md b/src/deepwork/standard/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md
new file mode 100644
index 00000000..fc321212
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/learnings/expert_prompts_must_emphasize_domain_focus.md
@@ -0,0 +1,42 @@
+---
+name: Expert prompts must emphasize domain focus
+last_updated: 2026-02-01
+summarized_result: |
+ When invoking experts for tasks like PR review, prompts must explicitly tell
+ experts to ONLY comment on their domain and not provide generic feedback.
+ Without this, experts provide overlapping generic reviews instead of unique
+ specialized perspectives.
+---
+
+## Context
+
+During a PR review using the `/review_pr` job, both the `deepwork-jobs` and `experts` experts were invoked to review changes to the experts system implementation. The expectation was that each expert would bring their unique domain knowledge to identify issues that generalist reviewers might miss.
+
+## Problem
+
+Both experts provided similar generic code review feedback. They identified the same issues (redundant exception handling, unused variables) rather than focusing on aspects specific to their domains:
+
+- The `deepwork-jobs` expert should have focused on how the experts system integrates with jobs, skill generation patterns, and hook systems
+- The `experts` expert should have focused on expert.yml schema design, topic/learning structure, and evolution strategies
+
+Instead, both provided overlapping feedback on general code quality issues.
+
+## Resolution
+
+Updated the expert prompts in the `review_pr` job steps to explicitly emphasize domain focus:
+
+```
+IMPORTANT: Only comment on aspects that fall within your area of expertise.
+Do not provide general code review feedback on things outside your domain -
+other experts will cover those areas. Use your specialized knowledge to
+identify issues that a generalist reviewer might miss.
+```
+
+Also added:
+- "Your domain: [brief description from discovery_description]" to remind experts of their focus
+- Request for feedback "from your expert perspective" throughout the prompt
+- Quality criteria including "Feedback is focused on each expert's specific domain of expertise"
+
+## Key Takeaway
+
+When designing job steps that invoke experts, the prompt must explicitly constrain experts to their domain. Without this, experts default to providing generic assistance rather than specialized insight. The value of the experts system comes from each expert contributing unique perspective - overlapping generic reviews wastes the multi-expert approach.
diff --git a/src/deepwork/standard/experts/experts/learnings/keep_experts_focused.md b/src/deepwork/standard/experts/experts/learnings/keep_experts_focused.md
new file mode 100644
index 00000000..b77186a0
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/learnings/keep_experts_focused.md
@@ -0,0 +1,33 @@
+---
+name: Keep Experts Focused
+last_updated: 2025-01-30
+summarized_result: |
+ Broad experts like "Rails" or "JavaScript" become too large and unfocused.
+ Better to create specific experts like "Rails ActiveJob" or "React Hooks".
+---
+
+## Context
+
+When initially designing the experts system, we considered creating broad,
+comprehensive experts that would cover entire technology stacks.
+
+## Investigation
+
+Testing showed that broad experts:
+- Generated overwhelming amounts of content
+- Struggled to provide specific, actionable guidance
+- Made it difficult to know which expert to invoke
+- Led to duplication across multiple broad experts
+
+## Resolution
+
+Adopted a principle of focused experts with clear boundaries:
+- Each expert covers a specific domain or technology subset
+- The discovery_description clearly indicates scope
+- Topics dive deep rather than wide
+- Learnings capture domain-specific insights
+
+## Key Takeaway
+
+An expert should be narrow enough that you can articulate its scope in 1-2
+sentences. If you can't, it's probably too broad.
diff --git a/src/deepwork/standard/experts/experts/learnings/prompt_hooks_not_executed.md b/src/deepwork/standard/experts/experts/learnings/prompt_hooks_not_executed.md
new file mode 100644
index 00000000..43ae0931
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/learnings/prompt_hooks_not_executed.md
@@ -0,0 +1,50 @@
+---
+name: Prompt Hooks Not Executed in Claude Code
+last_updated: 2025-01-30
+summarized_result: |
+ Claude Code parses but does not execute prompt-based stop hooks.
+ Only script/command hooks actually run. Use quality_criteria for validation.
+---
+
+## Context
+
+When implementing quality validation for job steps, developers often try
+to use inline `prompt` or `prompt_file` hooks for validation.
+
+## Investigation
+
+Testing revealed that Claude Code's hook system only executes `command` type
+hooks in the settings.json hooks configuration. Prompt-based hooks are parsed
+by DeepWork but not rendered into the skill's hook frontmatter because they
+would not be executed.
+
+The template code explicitly filters:
+```jinja
+{%- set script_hooks = event_hooks | selectattr("type", "equalto", "script") | list %}
+```
+
+## Resolution
+
+Two recommended approaches for quality validation:
+
+1. **Use `quality_criteria` field** (preferred):
+ ```yaml
+ quality_criteria:
+ - "Each competitor has description"
+ - "Sources are cited"
+ ```
+ This generates instructions for sub-agent review, which works reliably.
+
+2. **Use script hooks** for objective validation:
+ ```yaml
+ hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+ ```
+ Scripts actually execute and can fail the step.
+
+## Key Takeaway
+
+For subjective quality checks, use the `quality_criteria` field which triggers
+sub-agent review. For objective checks (tests, linting), use script hooks.
+Avoid prompt hooks until Claude Code supports them.
diff --git a/src/deepwork/standard/experts/experts/learnings/review_pr_efficiency_improvements.md b/src/deepwork/standard/experts/experts/learnings/review_pr_efficiency_improvements.md
new file mode 100644
index 00000000..8ede78c5
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/learnings/review_pr_efficiency_improvements.md
@@ -0,0 +1,74 @@
+---
+name: Review PR job efficiency improvements
+last_updated: 2026-02-01
+summarized_result: |
+ The review_pr job used excessive tokens by sending full file contents to all
+ experts instead of using the per-expert file segmentation from the relevance
+ step. Also, quality validation sub-agents add overhead that may not be needed
+ for simple checklist verification.
+---
+
+## Context
+
+During execution of the `/review_pr` job on PR #192, the workflow completed successfully but used significantly more tokens and time than necessary.
+
+## Problems Identified
+
+### 1. Ignored per-expert file segmentation
+
+The `check_relevance` step correctly identified which files each expert should review:
+- deepwork-jobs: schema, parser, generator, CLI, templates, tests
+- experts: same files (significant overlap in this case)
+
+But the `deep_review` step ignored this segmentation and sent ALL files to BOTH experts. Each expert received ~1000+ lines of code when they should have received only their relevant subset.
+
+### 2. Full file contents vs. targeted excerpts
+
+Experts received full file contents when often they only needed:
+- The diff (what changed)
+- Specific sections relevant to their domain
+- Perhaps function signatures for context
+
+Sending full test files (500+ lines) to review production code changes is wasteful.
+
+### 3. Quality validation sub-agent overhead
+
+Each step spawned a Haiku sub-agent purely to verify a checklist of 3-5 criteria. This adds:
+- Network round-trip latency
+- Token overhead for the sub-agent context
+- Complexity
+
+For simple boolean criteria checks, the main agent could self-verify.
+
+### 4. Redundant data fetching
+
+The PR diff was fetched separately in both `check_relevance` and `deep_review` steps instead of being passed along or cached.
+
+## Recommendations
+
+### For step instructions:
+
+1. **Use inline bash completion `$(command)`**: Instead of the orchestrating agent reading
+ data and passing it to sub-agents, embed commands directly in prompts:
+ ```
+ $(gh pr diff)
+ $(gh pr diff --name-only)
+ ```
+ This executes when the sub-agent spawns, avoiding token overhead in the main conversation.
+
+2. **Use relevance segmentation**: The deep_review step should explicitly use the "Relevant files" list from each expert's relevance assessment to scope what content is sent to each expert.
+
+3. **Prefer diffs over full files**: For review tasks, send the diff plus minimal context (function signatures, class definitions) rather than entire files.
+
+4. **Skip validation sub-agents for simple criteria**: If quality criteria are simple boolean checks (e.g., "output file exists", "all experts invoked"), the main agent can verify these directly.
+
+### For job design:
+
+1. **Let sub-agents fetch their own data**: Using `$(command)` in prompts means sub-agents
+ get fresh data directly without the orchestrator reading and forwarding it.
+
+2. **Consider expert-specific prompts**: Generate different prompts for each expert containing only their relevant files, rather than one massive prompt for all.
+
+## Key Takeaway
+
+The experts system's value comes from specialized, focused review. Sending all content to all experts defeats this purpose and wastes tokens. Job steps that invoke multiple experts should segment the workload based on each expert's declared relevance.
diff --git a/.deepwork/jobs/deepwork_jobs/templates/agents.md.template b/src/deepwork/standard/experts/experts/templates/agents.md.template
similarity index 88%
rename from .deepwork/jobs/deepwork_jobs/templates/agents.md.template
rename to src/deepwork/standard/experts/experts/templates/agents.md.template
index c160921d..83681c0e 100644
--- a/.deepwork/jobs/deepwork_jobs/templates/agents.md.template
+++ b/src/deepwork/standard/experts/experts/templates/agents.md.template
@@ -1,4 +1,4 @@
-# Project Context for [Job Name]
+# Project Context for [Workflow Name]
## Codebase Structure
@@ -15,9 +15,9 @@
### File Organization
- [Pattern]: Reference `path/to/pattern/`
-## Job-Specific Context
+## Workflow-Specific Context
-### [Job Name]
+### [Workflow Name]
#### [Step Name]
- [Learning]: Reference `relevant/file.ext`
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/templates/doc_spec.md.template b/src/deepwork/standard/experts/experts/templates/doc_spec.md.template
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_jobs/templates/doc_spec.md.template
rename to src/deepwork/standard/experts/experts/templates/doc_spec.md.template
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/templates/step_instruction.md.example b/src/deepwork/standard/experts/experts/templates/step_instruction.md.example
similarity index 100%
rename from src/deepwork/standard_jobs/deepwork_jobs/templates/step_instruction.md.example
rename to src/deepwork/standard/experts/experts/templates/step_instruction.md.example
diff --git a/.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template b/src/deepwork/standard/experts/experts/templates/step_instruction.md.template
similarity index 79%
rename from .deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template
rename to src/deepwork/standard/experts/experts/templates/step_instruction.md.template
index ddd19213..0486aaa9 100644
--- a/.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template
+++ b/src/deepwork/standard/experts/experts/templates/step_instruction.md.template
@@ -9,7 +9,7 @@
[Detailed instructions for completing this step, based on:
- The step's purpose
- Expected inputs and outputs
-- The job's overall context
+- The workflow's overall context
]
### Process
@@ -51,8 +51,7 @@
- [Quality criterion 1]
- [Quality criterion 2]
- [Quality criterion 3]
-- When all criteria are met, include `✓ Quality Criteria Met` in your response
## Context
-[Provide context from the job's overall description to help understand why this step matters and how it fits into the bigger picture]
+[Provide context from the workflow's overall description to help understand why this step matters and how it fits into the bigger picture]
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/templates/job.yml.example b/src/deepwork/standard/experts/experts/templates/workflow.yml.example
similarity index 80%
rename from src/deepwork/standard_jobs/deepwork_jobs/templates/job.yml.example
rename to src/deepwork/standard/experts/experts/templates/workflow.yml.example
index 7cc6e3bb..b3558618 100644
--- a/src/deepwork/standard_jobs/deepwork_jobs/templates/job.yml.example
+++ b/src/deepwork/standard/experts/experts/templates/workflow.yml.example
@@ -1,6 +1,6 @@
-# Example: Competitive Research Job
+# Example: Competitive Research Workflow
#
-# This is a complete example of a job.yml file for reference.
+# This is a complete example of a workflow.yml file for reference.
name: competitive_research
version: "1.0.0"
@@ -12,7 +12,7 @@ description: |
changelog:
- version: "1.0.0"
- changes: "Initial job creation"
+ changes: "Initial workflow creation"
steps:
- id: identify_competitors
@@ -39,14 +39,10 @@ steps:
- research_notes.md
dependencies:
- identify_competitors
- hooks:
- after_agent:
- - prompt: |
- Verify the research meets criteria:
- 1. Each competitor has at least 3 data points
- 2. Sources are cited
- 3. Information is current (within last year)
- If ALL criteria are met, include `✓ Quality Criteria Met`.
+ quality_criteria:
+ - "Each competitor has at least 3 data points"
+ - "Sources are cited"
+ - "Information is current (within last year)"
- id: comparative_analysis
name: "Comparative Analysis"
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/templates/job.yml.template b/src/deepwork/standard/experts/experts/templates/workflow.yml.template
similarity index 65%
rename from src/deepwork/standard_jobs/deepwork_jobs/templates/job.yml.template
rename to src/deepwork/standard/experts/experts/templates/workflow.yml.template
index 7dcf34e9..9fc65955 100644
--- a/src/deepwork/standard_jobs/deepwork_jobs/templates/job.yml.template
+++ b/src/deepwork/standard/experts/experts/templates/workflow.yml.template
@@ -1,13 +1,13 @@
-# DeepWork Job Specification Template
+# DeepWork Workflow Specification Template
#
-# This template shows the structure of a job.yml file.
+# This template shows the structure of a workflow.yml file.
# Replace placeholders in [brackets] with actual values.
-name: [job_name]
+name: [workflow_name]
version: "1.0.0"
-summary: "[Brief one-line summary of what this job accomplishes - max 200 chars]"
+summary: "[Brief one-line summary of what this workflow accomplishes - max 200 chars]"
description: |
- [Detailed multi-line description of the job's purpose, process, and goals.
+ [Detailed multi-line description of the workflow's purpose, process, and goals.
This should explain:
- What problem this workflow solves
@@ -18,7 +18,7 @@ description: |
changelog:
- version: "1.0.0"
- changes: "Initial job creation"
+ changes: "Initial workflow creation"
steps:
- id: [step_id]
@@ -35,15 +35,11 @@ steps:
- [output_filename_or_path] # e.g., "report.md" or "reports/analysis.md"
dependencies: [] # List of step IDs that must complete first
# Optional: Delegate to a specific agent type (uses context: fork)
- # agent: general-purpose # or other agent type
- # Optional: Quality validation hooks
- hooks:
- after_agent:
- - prompt: |
- Verify this step's output meets quality criteria:
- 1. [Criterion 1]
- 2. [Criterion 2]
- If ALL criteria are met, include `✓ Quality Criteria Met`.
+ # agent: experts # or other agent type
+ # Optional: Quality validation criteria
+ quality_criteria:
+ - "[Criterion 1]"
+ - "[Criterion 2]"
- id: [another_step]
name: "[Another Step]"
diff --git a/src/deepwork/standard/experts/experts/topics/discovery_descriptions.md b/src/deepwork/standard/experts/experts/topics/discovery_descriptions.md
new file mode 100644
index 00000000..f68112b9
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/discovery_descriptions.md
@@ -0,0 +1,83 @@
+---
+name: Writing Discovery Descriptions
+keywords:
+ - discovery
+ - description
+ - routing
+ - selection
+last_updated: 2025-01-30
+---
+
+# Writing Effective Discovery Descriptions
+
+The `discovery_description` determines when your expert gets invoked. It's the
+"elevator pitch" that helps the system route queries to the right expert.
+
+## Purpose
+
+Discovery descriptions are used by other parts of the system to decide:
+- Whether to suggest this expert for a task
+- Which expert to invoke when multiple could apply
+- How to present the expert to users
+
+## Anatomy of a Good Description
+
+```yaml
+discovery_description: |
+ Ruby on Rails ActiveJob - background job processing including
+ queue configuration, retry strategies, error handling, and
+ integration with queue backends like Sidekiq and SolidQueue.
+```
+
+Components:
+1. **Domain identifier**: "Ruby on Rails ActiveJob"
+2. **Core capability**: "background job processing"
+3. **Specific coverage**: "queue configuration, retry strategies..."
+4. **Boundaries**: "integration with queue backends like..."
+
+## Guidelines
+
+### Be Specific
+Bad: "Helps with background jobs"
+Good: "Rails ActiveJob background processing - queues, retries, error handling"
+
+### Include Key Terms
+Include terms users would search for. If someone asks about "Sidekiq retries",
+the description should contain those words.
+
+### Set Boundaries
+Indicate what's in and out of scope. "...including X, Y, Z" signals coverage.
+
+### Keep It Scannable
+1-3 sentences max. The system needs to quickly evaluate relevance.
+
+### Avoid Marketing Speak
+Bad: "The ultimate guide to mastering background jobs"
+Good: "ActiveJob configuration, error handling, and queue backend integration"
+
+## Examples
+
+### Too Vague
+```yaml
+discovery_description: Helps with Rails development
+```
+
+### Too Narrow
+```yaml
+discovery_description: How to configure exponential backoff in ActiveJob
+```
+
+### Just Right
+```yaml
+discovery_description: |
+ Rails ActiveJob expertise - background job processing, queue
+ configuration, retry strategies, error handling, and integration
+ with Sidekiq, SolidQueue, and other queue backends.
+```
+
+## Testing Your Description
+
+Ask yourself:
+1. If I had this problem, would I find this expert?
+2. Does it differentiate from similar experts?
+3. Can I tell what's covered in 5 seconds?
diff --git a/src/deepwork/standard/experts/experts/topics/expert_design_patterns.md b/src/deepwork/standard/experts/experts/topics/expert_design_patterns.md
new file mode 100644
index 00000000..3ce13a61
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/expert_design_patterns.md
@@ -0,0 +1,69 @@
+---
+name: Expert Design Patterns
+keywords:
+ - patterns
+ - structure
+ - organization
+ - best practices
+last_updated: 2025-01-30
+---
+
+# Expert Design Patterns
+
+Common patterns for structuring effective experts.
+
+## The Layered Knowledge Pattern
+
+Structure expertise from general to specific:
+
+1. **full_expertise**: Core concepts, decision frameworks, common patterns
+2. **topics/**: Deep dives into specific subjects
+3. **learnings/**: Concrete experiences and edge cases
+
+This mirrors how humans learn - start with foundations, then specialize.
+
+## The Problem-Solution Pattern
+
+For domains centered around solving problems:
+
+- **full_expertise**: Problem categories, diagnostic approaches, solution frameworks
+- **topics/**: Specific problem types with detailed solutions
+- **learnings/**: Real debugging sessions and unexpected fixes
+
+Works well for: troubleshooting guides, error handling, debugging domains.
+
+## The Reference Pattern
+
+For domains with lots of factual information:
+
+- **full_expertise**: Overview, when to use what, quick reference
+- **topics/**: Detailed reference on specific APIs, configs, options
+- **learnings/**: Gotchas and undocumented behaviors
+
+Works well for: API documentation, configuration guides, tool references.
+
+## The Process Pattern
+
+For domains with sequential workflows:
+
+- **full_expertise**: Overall process, decision points, success criteria
+- **topics/**: Detailed steps for each phase
+- **learnings/**: Process failures and improvements
+
+Works well for: deployment procedures, review processes, onboarding.
+
+## Anti-Patterns to Avoid
+
+### The Kitchen Sink
+Cramming everything into full_expertise. If it's over 5 pages, split into topics.
+
+### The Empty Shell
+Creating expert.yml with minimal content and empty topics/learnings folders.
+Start with meaningful content or don't create the expert yet.
+
+### The Stale Expert
+Never updating after initial creation. Set a reminder to review quarterly.
+
+### The Duplicate Expert
+Creating overlapping experts. Better to have one comprehensive expert than
+several fragmented ones.
diff --git a/src/deepwork/standard/experts/experts/topics/hooks_and_validation.md b/src/deepwork/standard/experts/experts/topics/hooks_and_validation.md
new file mode 100644
index 00000000..96d2d037
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/hooks_and_validation.md
@@ -0,0 +1,179 @@
+---
+name: Hooks and Validation
+keywords:
+ - hooks
+ - validation
+ - quality
+ - lifecycle
+last_updated: 2025-02-01
+---
+
+# Hooks and Validation
+
+How to use lifecycle hooks for quality validation in DeepWork workflows.
+
+## Lifecycle Hook Events
+
+DeepWork supports three generic hook events:
+
+| Event | When it fires | Use case |
+|-------|---------------|----------|
+| `after_agent` | After agent finishes responding | Quality validation, output verification |
+| `before_tool` | Before agent uses a tool | Pre-tool checks, validation |
+| `before_prompt` | When user submits a prompt | Session setup, context loading |
+
+## Platform Mapping
+
+Hooks are mapped to platform-specific event names:
+
+| Generic | Claude Code |
+|---------|-------------|
+| `after_agent` | `Stop`, `SubagentStop` |
+| `before_tool` | `PreToolUse` |
+| `before_prompt` | `UserPromptSubmit` |
+
+Note: Gemini CLI does not support skill-level hooks (only global hooks).
+
+## Hook Action Types
+
+### Inline Prompt
+
+Best for simple validation criteria:
+
+```yaml
+hooks:
+ after_agent:
+ - prompt: |
+ Verify the output meets these criteria:
+ 1. Contains at least 5 competitors
+ 2. Each has a description
+ 3. Sources are cited
+```
+
+**Important**: Prompt hooks are currently parsed but NOT executed by Claude Code.
+This is a documented limitation. Use script hooks for actual enforcement.
+
+### Prompt File
+
+For detailed/reusable criteria:
+
+```yaml
+hooks:
+ after_agent:
+ - prompt_file: hooks/quality_check.md
+```
+
+The prompt file is read and its content is embedded in the generated skill.
+Same limitation applies - parsed but not executed.
+
+### Script Hook
+
+For programmatic validation (actually executed):
+
+```yaml
+hooks:
+ after_agent:
+ - script: hooks/run_tests.sh
+```
+
+Scripts are shell scripts that can:
+- Run test suites
+- Lint output files
+- Check for required content
+- Validate file formats
+
+Exit code 0 = pass, non-zero = fail.
+
+## Script Hook Example
+
+Create `.deepwork/experts/[expert]/workflows/[workflow]/hooks/validate.sh`:
+
+```bash
+#!/bin/bash
+# Validate research output
+
+OUTPUT_FILE="research_notes.md"
+
+if [ ! -f "$OUTPUT_FILE" ]; then
+ echo "ERROR: $OUTPUT_FILE not found"
+ exit 1
+fi
+
+# Check minimum content
+LINES=$(wc -l < "$OUTPUT_FILE")
+if [ "$LINES" -lt 50 ]; then
+ echo "ERROR: Output has only $LINES lines, expected at least 50"
+ exit 1
+fi
+
+echo "Validation passed"
+exit 0
+```
+
+Make it executable:
+```bash
+chmod +x .deepwork/experts/[expert]/workflows/[workflow]/hooks/validate.sh
+```
+
+## Combining Multiple Hooks
+
+```yaml
+hooks:
+ after_agent:
+ - script: hooks/lint.sh
+ - script: hooks/run_tests.sh
+ - prompt: "Verify documentation is complete"
+```
+
+Hooks run in order. Script hooks are executed; prompt hooks are for reference.
+
+## Quality Criteria Alternative
+
+For simple validation, use declarative `quality_criteria` instead of hooks:
+
+```yaml
+steps:
+ - id: research
+ quality_criteria:
+ - "**Data Coverage**: Each competitor has at least 3 data points"
+ - "**Source Attribution**: All facts are cited"
+ - "**Relevance**: All competitors are in the target market"
+```
+
+Quality criteria are rendered in the skill with instructions to use a
+sub-agent (Haiku model) for review:
+
+1. Agent completes work
+2. Spawns sub-agent to review against criteria
+3. Fixes any issues identified
+4. Repeats until sub-agent confirms all criteria pass
+
+This is the recommended approach for most validation needs - it's more
+flexible than scripts and actually works with Claude Code.
+
+## When to Use Each Approach
+
+| Approach | When to use |
+|----------|-------------|
+| `quality_criteria` | Most cases - subjective quality checks |
+| Script hooks | Objective checks (tests, linting, format validation) |
+| Prompt hooks | Documentation only (not currently executed) |
+
+## Generated Skill Output
+
+For script hooks, the generated skill includes:
+
+```yaml
+hooks:
+ Stop:
+ - hooks:
+ - type: command
+ command: ".deepwork/experts/expert_name/workflows/workflow_name/hooks/validate.sh"
+ SubagentStop:
+ - hooks:
+ - type: command
+ command: ".deepwork/experts/expert_name/workflows/workflow_name/hooks/validate.sh"
+```
+
+Both `Stop` and `SubagentStop` are registered so hooks fire for both
+the main agent and any sub-agents.
diff --git a/src/deepwork/standard/experts/experts/topics/skill_generation.md b/src/deepwork/standard/experts/experts/topics/skill_generation.md
new file mode 100644
index 00000000..ea943b17
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/skill_generation.md
@@ -0,0 +1,163 @@
+---
+name: Skill Generation
+keywords:
+ - sync
+ - templates
+ - jinja
+ - skills
+ - commands
+last_updated: 2025-02-01
+---
+
+# Skill Generation
+
+How DeepWork generates platform-specific skills from expert definitions.
+
+## The Sync Process
+
+Running `deepwork sync`:
+
+1. Loads `config.yml` to get configured platforms
+2. Discovers all expert directories in `.deepwork/experts/`
+3. For each expert, discovers workflows in `workflows/` subdirectory
+4. Parses each `workflow.yml` into dataclasses
+5. For each workflow and platform, generates skills using Jinja2 templates
+6. Writes skill files to platform-specific directories
+7. Syncs hooks and permissions to platform settings
+
+## Generated Skill Types
+
+### Workflow Meta-Skill
+- One per workflow
+- Routes user intent to appropriate step
+- Lists available steps in the workflow
+- Claude: `.claude/skills/[expert-name].[workflow-name]/SKILL.md`
+
+### Step Skills
+- One per step
+- Contains full instructions, inputs, outputs
+- Includes workflow position and navigation
+- Claude: `.claude/skills/[expert-name].[step-id]/SKILL.md`
+
+## Skill Naming Convention
+
+Skills use 2-part naming: `{expert-name}.{step-id}`
+
+Examples:
+- `experts.define` - The define step in the experts expert
+- `experts.implement` - The implement step
+- `deepwork-rules.define` - The define step in deepwork-rules expert
+
+## Template Variables
+
+Templates receive rich context including:
+
+**Expert Context**:
+- `expert_name` - The expert identifier (hyphenated)
+
+**Workflow Context**:
+- `workflow_name`, `workflow_version`, `workflow_summary`, `workflow_description`
+- `total_steps`, `execution_entries`
+
+**Step Context**:
+- `step_id`, `step_name`, `step_description`
+- `instructions_content` (full markdown from instructions file)
+- `user_inputs`, `file_inputs`, `outputs`, `dependencies`
+- `exposed`, `agent`
+- `workflow_step_number`, `workflow_total_steps`
+- `next_step`, `prev_step`
+
+**Quality & Hooks**:
+- `quality_criteria` (array of strings)
+- `hooks` (dict by platform event name)
+
+## Template Location
+
+Templates live in `src/deepwork/templates/[platform]/`:
+
+```
+templates/
+├── claude/
+│ ├── skill-workflow-meta.md.jinja
+│ ├── skill-workflow-step.md.jinja
+│ └── agent-expert.md.jinja
+└── gemini/
+ ├── skill-workflow-meta.toml.jinja
+ └── skill-workflow-step.toml.jinja
+```
+
+## Platform Differences
+
+**Claude Code**:
+- Markdown format with YAML frontmatter
+- Uses `---` delimited frontmatter for metadata
+- Hook events: `Stop`, `SubagentStop`, `PreToolUse`, `UserPromptSubmit`
+- Skills directory: `.claude/skills/`
+- Agents directory: `.claude/agents/`
+
+**Gemini CLI**:
+- TOML format
+- No skill-level hooks (global only)
+- Skills directory: `.gemini/skills/`
+
+## The Generator Class
+
+`ExpertGenerator` in `core/experts_generator.py`:
+
+```python
+generator = ExpertGenerator()
+
+# Generate all skills for an expert
+paths = generator.generate_all_expert_skills(
+ expert=expert_definition,
+ adapter=claude_adapter,
+ output_dir=project_path,
+ project_root=project_path
+)
+
+# Generate single step skill
+path = generator.generate_workflow_step_skill(
+ expert=expert_def,
+ workflow=workflow,
+ step=step,
+ adapter=adapter,
+ output_dir=output_dir
+)
+```
+
+## Doc Spec Integration
+
+When outputs reference doc specs, the generator:
+
+1. Loads doc spec file using `DocSpecParser`
+2. Extracts quality criteria, target audience, example document
+3. Includes this context in template variables
+4. Generated skill displays doc spec requirements inline
+
+## Skill Frontmatter
+
+Claude skills have YAML frontmatter:
+
+```yaml
+---
+name: experts.define
+description: "Step description"
+user-invocable: false # when exposed: false
+context: fork # when agent is specified
+agent: experts
+hooks:
+ Stop:
+ - hooks:
+ - type: command
+ command: ".deepwork/experts/experts/workflows/new_workflow/hooks/validate.sh"
+---
+```
+
+## Permissions Syncing
+
+After generating skills, adapters sync permissions:
+
+- Base permissions (read `.deepwork/tmp/**`)
+- Skill invocation permissions (`Skill(expert.step_id)`)
+
+Permissions are added to `.claude/settings.json` in the `permissions.allow` array.
diff --git a/src/deepwork/standard/experts/experts/topics/step_delegation.md b/src/deepwork/standard/experts/experts/topics/step_delegation.md
new file mode 100644
index 00000000..042f68ea
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/step_delegation.md
@@ -0,0 +1,145 @@
+---
+name: Step Delegation to Experts
+keywords:
+ - agent
+ - expert
+ - delegation
+ - context fork
+ - sub-agent
+last_updated: 2025-02-01
+---
+
+# Step Delegation to Experts
+
+How to make workflow steps run via experts using the `agent` field.
+
+## The `agent` Field
+
+The `agent` field in a step definition specifies which expert should execute that step. This is the primary mechanism for making steps "run via" an expert.
+
+```yaml
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Creates a workflow.yml specification..."
+ instructions_file: steps/define.md
+ agent: experts # <-- This step runs via the experts expert
+ outputs:
+ - workflow.yml
+```
+
+## What Happens When You Set `agent`
+
+When a step has `agent: expert-name`, the generated skill includes:
+
+1. **`context: fork`** - The step runs in an isolated context (sub-agent)
+2. **`agent: expert-name`** - The expert's knowledge is loaded into that context
+
+The resulting skill frontmatter looks like:
+
+```yaml
+---
+name: my-expert.define
+description: "Creates a workflow.yml specification..."
+context: fork
+agent: experts
+---
+```
+
+## How Expert Knowledge Flows
+
+When the skill runs:
+
+1. A forked context (sub-agent) is created
+2. The expert file (e.g., `.claude/agents/dwe_experts.md`) is loaded
+3. All expert topics, learnings, and domain knowledge become available
+4. The step instructions execute with this enhanced context
+5. The sub-agent completes and returns to the parent context
+
+## Real-World Example: experts
+
+The `experts` expert itself uses expert delegation. The new_workflow workflow steps run via the `experts` expert:
+
+```yaml
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Creates a workflow.yml specification..."
+ instructions_file: steps/define.md
+ agent: experts # Runs with workflow schema knowledge
+ outputs:
+ - file: workflow.yml
+ doc_spec: .deepwork/doc_specs/workflow_spec.md
+
+ - id: review_spec
+ name: "Review Workflow Specification"
+ description: "Reviews workflow.yml against quality criteria..."
+ instructions_file: steps/review_spec.md
+ agent: experts # Same expert, different step
+ inputs:
+ - file: workflow.yml
+ from_step: define
+ outputs:
+ - file: workflow.yml
+ doc_spec: .deepwork/doc_specs/workflow_spec.md
+
+ - id: implement
+ name: "Implement Workflow Steps"
+ description: "Generates step instruction files..."
+ instructions_file: steps/implement.md
+ agent: experts # Expert knowledge helps write good instructions
+ inputs:
+ - file: workflow.yml
+ from_step: review_spec
+ outputs:
+ - steps/
+```
+
+## When to Use Expert Delegation
+
+Use `agent: expert-name` when:
+
+1. **Domain expertise is needed** - The step requires specialized knowledge that an expert provides
+2. **Consistency across steps** - Multiple steps in a workflow should use the same expert context
+3. **Complex validation** - The expert has knowledge about quality criteria or best practices
+4. **Template awareness** - The expert knows about file formats, schemas, or conventions
+
+## Available Experts
+
+Standard experts that ship with DeepWork:
+
+- `experts` - Expert on expert creation, workflow definitions, skill generation
+- `deepwork-rules` - Expert on rule creation and file-change triggers
+
+Custom experts can be created in `.deepwork/experts/` and referenced by name.
+
+## Expert vs. Sub-Agent Quality Review
+
+Note the difference between:
+
+1. **Expert delegation** (`agent: experts`) - The entire step runs with expert knowledge loaded
+2. **Sub-agent quality review** (`quality_criteria` field) - A Haiku sub-agent validates outputs against criteria
+
+These can be combined - a step can run via an expert AND have quality criteria validated by a sub-agent:
+
+```yaml
+steps:
+ - id: implement
+ instructions_file: steps/implement.md
+ agent: experts # Expert knowledge during execution
+ quality_criteria: # Sub-agent validates outputs
+ - "All instruction files are complete"
+ - "No placeholder content remains"
+```
+
+## Troubleshooting
+
+**Step not using expert knowledge?**
+- Verify `agent: expert-name` is set in workflow.yml
+- Run `deepwork sync` to regenerate skills
+- Check the generated skill has `agent:` in its frontmatter
+
+**Expert not found?**
+- Ensure the expert exists in `.deepwork/experts/[expert-name]/`
+- Run `deepwork sync` to generate the expert agent file
+- Check `.claude/agents/dwe_[expert-name].md` exists
diff --git a/src/deepwork/standard/experts/experts/topics/step_instructions.md b/src/deepwork/standard/experts/experts/topics/step_instructions.md
new file mode 100644
index 00000000..df2594c1
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/step_instructions.md
@@ -0,0 +1,187 @@
+---
+name: Writing Step Instructions
+keywords:
+ - instructions
+ - steps
+ - markdown
+ - writing
+ - best practices
+last_updated: 2025-02-01
+---
+
+# Writing Step Instructions
+
+Best practices for writing effective step instruction files.
+
+## File Location
+
+Step instructions live in `.deepwork/experts/[expert]/workflows/[workflow]/steps/[step_id].md`.
+
+The path is specified in workflow.yml via `instructions_file`:
+```yaml
+steps:
+ - id: identify_competitors
+ instructions_file: steps/identify_competitors.md
+```
+
+## Recommended Structure
+
+### 1. Objective Section
+
+Start with a clear objective statement:
+
+```markdown
+## Objective
+
+Create a comprehensive list of competitors in the target market by
+systematically researching industry players and their offerings.
+```
+
+### 2. Task Section
+
+Detailed step-by-step process:
+
+```markdown
+## Task
+
+### Step 1: Understand the Market
+
+Ask structured questions to gather context:
+- What industry or market segment?
+- What product category?
+- Geographic scope?
+
+### Step 2: Research Sources
+
+Search for competitors using:
+1. Industry databases and reports
+2. Google searches for market leaders
+3. Customer review sites
+...
+```
+
+### 3. Input Handling
+
+If the step has user inputs, explicitly request them:
+
+```markdown
+## Inputs
+
+Before proceeding, gather the following from the user:
+- **market_segment**: Target market for analysis
+- **product_category**: Specific product/service category
+
+Ask questions one at a time and wait for responses.
+```
+
+### 4. Output Format
+
+Show what good output looks like:
+
+```markdown
+## Output Format
+
+Create `competitors_list.md` with the following structure:
+
+```markdown
+# Competitors List
+
+## Market: [market_segment]
+
+### Competitor 1: Acme Corp
+- **Website**: acme.com
+- **Description**: Brief overview
+- **Key Products**: Product A, Product B
+```
+
+### 5. Quality Criteria
+
+Define how to verify the step is complete:
+
+```markdown
+## Quality Criteria
+
+- At least 5 competitors identified
+- Each competitor has description and key products
+- Sources are cited for all information
+- List is relevant to the specified market
+```
+
+## Key Phrases
+
+### "Ask structured questions"
+
+When gathering user input, use this phrase and ask questions one at a time:
+
+```markdown
+Ask structured questions to understand the user's requirements.
+Start with: What is your target market?
+```
+
+**Important**: Present one question, wait for the response, then ask the next.
+Do not output all questions as a numbered list - this results in poor UX
+where the user must scroll through and answer multiple questions at once.
+
+### "Use the Skill tool to invoke"
+
+For workflow continuation:
+
+```markdown
+## On Completion
+
+1. Verify outputs are created
+2. Use the Skill tool to invoke `/experts.next_step`
+```
+
+## Supplementary Files
+
+Place additional reference materials in `steps/`:
+
+```
+steps/
+├── identify_competitors.md
+├── research_competitors.md
+└── competitor_template.md # supplementary reference
+```
+
+Reference them using full paths:
+```markdown
+Use the template in `.deepwork/experts/my_expert/workflows/competitive_research/steps/competitor_template.md`
+to structure each competitor profile.
+```
+
+## Anti-Patterns to Avoid
+
+### Vague Instructions
+Bad: "Research the competitors"
+Good: "Search each competitor's website, LinkedIn, and review sites to gather..."
+
+### Missing Outputs
+Bad: "Create a report"
+Good: "Create `research_notes.md` with sections for each competitor..."
+
+### Skipping Inputs
+Bad: Assume inputs are available
+Good: "Read `competitors_list.md` from the previous step. If it doesn't exist..."
+
+### Generic Quality Criteria
+Bad: "Output should be good quality"
+Good: "Each competitor profile includes at least 3 data points with sources"
+
+## Instruction Length
+
+- Keep instructions focused and actionable
+- Aim for 1-3 pages of content
+- Extract lengthy examples into separate template files
+- Use bullet points over paragraphs where appropriate
+
+## Variables in Instructions
+
+Instructions can reference workflow-level context. The generated skill includes:
+- Expert and workflow name
+- Step position in workflow
+- Dependencies and next steps
+- All inputs and outputs
+
+You don't need to repeat this metadata in instructions - it's included
+automatically in the generated skill.
diff --git a/src/deepwork/standard/experts/experts/topics/workflow_yml_schema.md b/src/deepwork/standard/experts/experts/topics/workflow_yml_schema.md
new file mode 100644
index 00000000..697bd6c3
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/topics/workflow_yml_schema.md
@@ -0,0 +1,181 @@
+---
+name: workflow.yml Schema Reference
+keywords:
+ - schema
+ - yaml
+ - validation
+ - fields
+ - structure
+ - workflow
+last_updated: 2025-02-01
+---
+
+# workflow.yml Schema Reference
+
+Complete reference for the workflow.yml specification file.
+
+## Required Fields
+
+### name
+- Type: string
+- Pattern: `^[a-z][a-z0-9_]*$`
+- Must start with lowercase letter, can contain lowercase letters, numbers, underscores
+- Must match the workflow folder name
+- Examples: `new_workflow`, `review_pr`, `learn`
+
+### version
+- Type: string
+- Pattern: `^\d+\.\d+\.\d+$`
+- Semantic versioning format
+- Examples: `1.0.0`, `2.1.3`
+
+### summary
+- Type: string
+- Length: 1-200 characters
+- Brief one-line description of the workflow
+- Used in skill descriptions and menus
+
+### steps
+- Type: array
+- Minimum items: 1
+- Each item is a step definition object
+
+## Optional Fields
+
+### description
+- Type: string
+- Multi-line detailed explanation
+- Included in generated skill files for context
+- Good for: problem solved, process overview, target users
+
+### execution_order
+- Type: array
+- Explicit step ordering with concurrent support
+- See Execution Order section
+
+### changelog
+- Type: array
+- Version history entries
+- Each entry has `version` (string) and `changes` (string)
+
+## Step Schema
+
+### Required Step Fields
+
+```yaml
+steps:
+ - id: identify_competitors # unique within expert, lowercase_underscores
+ name: "Identify Competitors" # human-readable
+ description: "Find and list..." # what it does
+ instructions_file: steps/identify.md # path relative to workflow dir
+ outputs: # at least one output
+ - competitors_list.md
+```
+
+### Optional Step Fields
+
+```yaml
+ - id: research
+ # ... required fields ...
+ inputs:
+ - name: market_segment
+ description: "Target market"
+ - file: competitors_list.md
+ from_step: identify_competitors
+ dependencies:
+ - identify_competitors
+ exposed: true # show in user menus
+ quality_criteria:
+ - "All competitors have descriptions"
+ - "Sources are cited"
+ agent: experts # delegate to expert (see Agent Delegation)
+ hooks:
+ after_agent:
+ - script: hooks/validate.sh
+```
+
+### Agent Delegation (The `agent` Field)
+
+The `agent` field specifies which expert should execute this step. When set:
+1. The generated skill includes `context: fork` (runs in isolated context)
+2. The generated skill includes `agent: [expert-name]` (loads that expert's knowledge)
+3. The step runs with all the expert's topics, learnings, and domain knowledge available
+
+**This is how you make a step "run via" an expert.**
+
+```yaml
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Creates a workflow.yml specification..."
+ instructions_file: steps/define.md
+ agent: experts # Runs with experts expert loaded
+ outputs:
+ - workflow.yml
+```
+
+Common agent values:
+- `experts` - Expert for DeepWork expert and workflow creation
+- `deepwork-rules` - Expert for DeepWork rule creation
+- Custom experts you've defined in `.deepwork/experts/`
+
+See the **Step Delegation** topic for detailed examples and patterns.
+
+## Output Formats
+
+Simple string:
+```yaml
+outputs:
+ - report.md
+ - data/
+```
+
+With doc spec:
+```yaml
+outputs:
+ - file: reports/analysis.md
+ doc_spec: .deepwork/doc_specs/analysis.md
+```
+
+Doc spec path must match pattern: `^\.deepwork/doc_specs/[a-z][a-z0-9_-]*\.md$`
+
+## Execution Order
+
+By default, steps execute in the order defined. Use `execution_order` for explicit control:
+
+Sequential:
+```yaml
+execution_order:
+ - identify
+ - research
+ - analyze
+ - report
+```
+
+Concurrent steps:
+```yaml
+execution_order:
+ - identify
+ - [research_a, research_b] # parallel execution
+ - synthesize
+```
+
+## Hook Schema
+
+```yaml
+hooks:
+ after_agent:
+ - prompt: "Verify criteria are met" # inline
+ - prompt_file: hooks/check.md # from file
+ - script: hooks/run_tests.sh # shell script
+```
+
+Each hook action must have exactly one of: `prompt`, `prompt_file`, or `script`.
+
+## Validation Rules
+
+1. Step IDs must be unique within the expert (across all workflows)
+2. Dependencies must reference existing step IDs
+3. File inputs with `from_step` must have that step in dependencies
+4. No circular dependencies allowed
+5. Execution order steps must reference existing step IDs
diff --git a/src/deepwork/standard/experts/experts/workflows/learn/steps/learn.md b/src/deepwork/standard/experts/experts/workflows/learn/steps/learn.md
new file mode 100644
index 00000000..9474d408
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/learn/steps/learn.md
@@ -0,0 +1,113 @@
+# Learn from Workflow Execution
+
+## Objective
+
+Reflect on the conversation to identify learnings from DeepWork workflow executions, improve workflow instructions with generalizable insights, and capture run-specific learnings in AGENTS.md files.
+
+## Task
+
+Analyze conversation history to extract learnings, then apply them:
+- **Generalizable learnings** -> Update workflow instruction files
+- **Bespoke learnings** (run-specific) -> Add to AGENTS.md in the working folder
+
+### Step 1: Analyze Conversation for Workflow Executions
+
+1. **Scan the conversation** for DeepWork slash commands (`/expert-name.step_id`)
+2. **Identify the target folder** - the deepest common folder for all work on the topic
+3. **If no workflow specified**, ask which workflow to learn from
+
+### Step 2: Identify Confusion and Inefficiency
+
+Review the conversation for:
+
+**Confusion signals**:
+- Unnecessary questions the agent asked
+- Misunderstandings about step requirements
+- Incorrect outputs needing correction
+- Ambiguous instructions causing wrong interpretations
+
+**Inefficiency signals**:
+- Extra iterations needed
+- Repeated information
+- Missing context
+- Unclear dependencies
+
+**Error patterns**:
+- Failed validations and why
+- Misunderstood quality criteria
+- Unhandled edge cases
+
+**Success patterns**:
+- What worked well
+- Efficient approaches worth preserving
+- Good examples to add to instructions
+
+### Step 3: Classify Learnings
+
+For each learning, determine if it is:
+
+**Generalizable** (update instructions):
+- Would help ANY future run of this workflow
+- Addresses unclear or missing guidance
+- Fixes incorrect assumptions
+- Adds helpful examples
+
+**Doc spec-related** (update doc spec files):
+- Improvements to document quality criteria
+- Changes to document structure or format
+
+**Bespoke** (add to AGENTS.md):
+- Specific to THIS project/codebase/run
+- Depends on local conventions
+- References specific files or paths
+- Would not apply to other uses
+
+### Step 4: Update Workflow Instructions (Generalizable)
+
+For each generalizable learning:
+
+1. Locate `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`
+2. Make targeted improvements:
+ - Add missing context
+ - Include helpful examples
+ - Clarify ambiguous instructions
+ - Update quality criteria
+3. Keep instructions concise - avoid redundancy
+4. Preserve structure (Objective, Task, Output Format, Quality Criteria)
+
+### Step 5: Update Doc Specs (if applicable)
+
+If doc spec-related learnings identified:
+
+1. Locate doc spec at `.deepwork/doc_specs/[doc_spec_name].md`
+2. Update quality_criteria, example document, or metadata as needed
+
+### Step 6: Create/Update AGENTS.md (Bespoke)
+
+1. Place in the deepest common folder for the topic
+2. Use file references instead of duplicating content: "See `path/to/file.ext` for [description]"
+
+**Good patterns** (references):
+```markdown
+- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
+- Configuration schema: Defined in `config/schema.json`
+```
+
+**Avoid** (duplicating):
+```markdown
+- API endpoints should return JSON with this format: { status: ..., data: ... }
+```
+
+### Step 7: Update Workflow Version and Sync
+
+If instructions were modified:
+
+1. Bump version in workflow.yml (patch for improvements, minor for quality criteria changes)
+2. Add changelog entry
+3. Run `deepwork sync`
+
+## Output
+
+- Updated workflow instructions (generalizable learnings)
+- Updated doc specs (if applicable)
+- AGENTS.md with bespoke learnings in the correct working folder
diff --git a/src/deepwork/standard/experts/experts/workflows/learn/workflow.yml b/src/deepwork/standard/experts/experts/workflows/learn/workflow.yml
new file mode 100644
index 00000000..94502252
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/learn/workflow.yml
@@ -0,0 +1,21 @@
+name: learn
+version: "1.0.0"
+summary: "Analyze conversation history to improve workflow instructions and capture learnings"
+
+description: |
+ Reflect on the conversation to identify learnings from DeepWork workflow executions,
+ improve workflow instructions with generalizable insights, and capture run-specific
+ learnings in AGENTS.md files.
+
+steps:
+ - id: learn
+ name: "Learn from Workflow Execution"
+ description: "Analyze conversation to extract learnings and improve instructions"
+ instructions_file: steps/learn.md
+ inputs:
+ - name: expert_name
+ description: "Name of the expert whose workflow to learn from (optional - will scan conversation if not provided)"
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/"
+ - "AGENTS.md"
+ exposed: true
diff --git a/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/define.md b/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/define.md
new file mode 100644
index 00000000..6ca8b721
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/define.md
@@ -0,0 +1,104 @@
+# Define Workflow Specification
+
+## Objective
+
+Create a `workflow.yml` specification file that defines a new DeepWork workflow by understanding the user's requirements through interactive questioning.
+
+## Task
+
+Guide the user through defining a workflow specification by asking structured questions. The output is **only** the `workflow.yml` file - step instruction files are created in the `implement` step.
+
+### Phase 1: Understand the Workflow Purpose
+
+Ask structured questions to understand the workflow:
+
+1. **Overall goal** - What complex task are they trying to accomplish? What domain (research, marketing, development, reporting)?
+
+2. **Success criteria** - What's the final deliverable? Who is the audience? What quality matters most?
+
+3. **Major phases** - What are the distinct stages from start to finish? Any dependencies between phases?
+
+4. **Expert context** - Which expert should this workflow belong to? Existing one or create new?
+
+### Phase 2: Detect Document-Oriented Workflows
+
+Check for document-focused patterns in the user's description:
+- Keywords: "report", "summary", "document", "monthly", "quarterly", "for stakeholders"
+- Final deliverable is a specific document type
+- Recurring documents with consistent structure
+
+**If detected**, offer to create a doc spec:
+1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first."
+2. Ask if they want to create a doc spec, use existing one, or skip
+
+**If creating a doc spec**, gather:
+- Document name and purpose
+- Target audience and frequency
+- Quality criteria (3-5, focused on the output document itself)
+- Document structure (sections, required elements)
+
+Create at `.deepwork/doc_specs/[doc_spec_name].md`.
+
+### Phase 3: Define Each Step
+
+For each major phase, gather:
+
+1. **Purpose** - What does this step accomplish? What are inputs and outputs?
+
+2. **Inputs**:
+ - User-provided parameters (e.g., topic, target audience)?
+ - Files from previous steps?
+
+3. **Outputs**:
+ - What files does this step produce?
+ - Format (markdown, YAML, JSON)?
+ - Where to save? (Use meaningful paths like `competitive_research/analysis.md`)
+ - Does this output have a doc spec?
+
+4. **Dependencies** - Which previous steps must complete first?
+
+5. **Process** - Key activities? Quality checks needed?
+
+6. **Agent Delegation** - Should this step run via a specific expert? Use `agent: experts` for domain-specific expertise.
+
+#### Output Path Guidelines
+
+- Place outputs in main repo, not dot-directories
+- Use workflow name as top-level folder for workflow-specific outputs
+- Include dates for periodic outputs that accumulate (monthly reports)
+- Omit dates for current-state outputs that get updated in place
+- Use `_dataroom` folders for supporting materials
+
+### Phase 4: Validate the Workflow
+
+After gathering all step information:
+
+1. **Review the flow** - Summarize the complete workflow, show how outputs feed into next steps
+2. **Check for gaps** - Undefined inputs? Unused outputs? Circular dependencies?
+3. **Confirm details** - Workflow name (lowercase_underscores), summary (max 200 chars), description (detailed), version (1.0.0)
+4. **Step ID uniqueness** - Step IDs must be unique within the expert, across all workflows
+
+### Phase 5: Create the Workflow
+
+Determine the expert and create the directory structure:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/steps
+```
+
+Create `workflow.yml` at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+**Validation rules**:
+- Workflow name: lowercase, underscores, no spaces (must match folder name)
+- Version: semantic versioning (1.0.0)
+- Summary: under 200 characters
+- Step IDs: unique within the expert, lowercase with underscores
+- Dependencies must reference existing steps
+- File inputs with `from_step` must be in dependencies
+- At least one output per step
+
+## Output
+
+**File**: `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+
+After creating the file, the next step will review it against quality criteria.
diff --git a/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/implement.md b/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/implement.md
new file mode 100644
index 00000000..129dc588
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/implement.md
@@ -0,0 +1,99 @@
+# Implement Workflow Steps
+
+## Objective
+
+Generate the step instruction files for each step in the validated `workflow.yml` specification, then sync the skills.
+
+## Task
+
+Read the `workflow.yml` specification and create all necessary files to make the workflow functional, then sync the commands.
+
+### Step 1: Create Directory Structure
+
+Create the workflow directories:
+
+```bash
+mkdir -p .deepwork/experts/[expert_name]/workflows/[workflow_name]/{steps,hooks}
+```
+
+This creates:
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/` - Main workflow directory
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/` - Step instruction files
+- `.deepwork/experts/[expert_name]/workflows/[workflow_name]/hooks/` - Custom validation scripts
+
+**Note**: If directory exists from define step, skip or just create missing subdirectories.
+
+### Step 2: Read and Validate the Specification
+
+1. Read `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`
+2. Validate: name, version, summary, steps are present
+3. Check dependencies reference existing steps, no circular dependencies
+4. Verify file inputs match dependencies
+
+### Step 3: Generate Step Instruction Files
+
+For each step in workflow.yml, create `.deepwork/experts/[expert_name]/workflows/[workflow_name]/steps/[step_id].md`.
+
+**Guidelines**:
+
+1. **Use the workflow description** - It provides crucial context
+2. **Be specific** - Tailor instructions to the step's purpose, not generic
+3. **Provide examples** - Show what good output looks like
+4. **Explain the "why"** - Help understand the step's role in the workflow
+5. **Ask structured questions** - Steps with user inputs MUST explicitly tell the agent to "ask structured questions"
+6. **Align with hooks** - If step has hooks, ensure quality criteria match
+
+Each instruction file should include:
+- **Objective** - What this step accomplishes
+- **Task** - Detailed process
+- **Output Format** - Examples of expected outputs
+- **Quality Criteria** - How to verify completion
+
+### Step 4: Verify workflow.yml Location
+
+Ensure `workflow.yml` is at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml`.
+
+### Step 5: Sync Skills
+
+Run:
+
+```bash
+deepwork sync
+```
+
+This generates skills in `.claude/skills/` (or appropriate platform directory).
+
+### Step 6: Consider Rules
+
+After implementing, consider whether **rules** would help this workflow's domain.
+
+**What are rules?** Automated guardrails that trigger when certain files change, ensuring:
+- Documentation stays in sync
+- Team guidelines are followed
+- Quality standards are maintained
+
+**When to suggest rules:**
+- Does this workflow produce outputs that other files depend on?
+- Are there docs that should update when outputs change?
+- Could changes impact other parts of the project?
+
+**Examples**:
+| Workflow Type | Potential Rule |
+|---------------|----------------|
+| API Design | "Update API docs when endpoint definitions change" |
+| Competitive Research | "Update strategy docs when competitor analysis changes" |
+| Feature Development | "Update changelog when feature files change" |
+
+If you identify helpful rules, explain what they would do and offer: "Would you like me to create this rule? I can run `/deepwork-rules.define` to set it up."
+
+**Note**: Not every workflow needs rules. Only suggest when genuinely helpful.
+
+## Completion Checklist
+
+- [ ] workflow.yml in correct location
+- [ ] All step instruction files created (not stubs)
+- [ ] Instructions are specific and actionable
+- [ ] Output examples provided
+- [ ] Quality criteria defined for each step
+- [ ] `deepwork sync` executed successfully
+- [ ] Considered relevant rules for this workflow
diff --git a/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md b/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md
new file mode 100644
index 00000000..880b8363
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/new_workflow/steps/review_workflow_spec.md
@@ -0,0 +1,72 @@
+# Review Workflow Specification
+
+## Objective
+
+Review the `workflow.yml` created in the define step against quality criteria, then iterate on fixes until all criteria pass.
+
+## Why This Step Exists
+
+The define step focuses on understanding user requirements. This review step ensures the specification meets quality standards before implementation. A fresh review catches issues that might be missed after being deeply involved in definition.
+
+## Task
+
+Review the workflow.yml against all quality criteria, fix any failures, and repeat until all pass.
+
+### Step 1: Read the Files
+
+Read the workflow specification:
+```
+.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml
+```
+
+### Step 2: Evaluate Against Quality Criteria
+
+Review the workflow.yml against these criteria:
+
+1. **Valid Identifier** - Workflow name is lowercase with underscores, matches folder name
+2. **Semantic Version** - Version follows X.Y.Z format (e.g., `1.0.0`)
+3. **Concise Summary** - Summary is under 200 characters and clearly describes the workflow
+4. **Rich Description** - Description is multi-line and explains: problem solved, process, expected outcomes, target users
+5. **Complete Steps** - Each step has: id, name, description, instructions_file, outputs
+6. **Unique Step IDs** - Step IDs are unique within the expert (across all workflows)
+7. **Valid Dependencies** - Dependencies reference existing step IDs with no circular references
+8. **Input Consistency** - File inputs with `from_step` reference a step in the dependencies array
+9. **Output Paths** - Outputs are valid filenames or paths
+
+For each criterion, determine PASS or FAIL. If FAIL, note the specific issue and fix.
+
+### Step 3: Fix Failed Criteria
+
+For each failed criterion, edit the workflow.yml:
+
+| Criterion | Common Issue | Fix |
+|-----------|-------------|-----|
+| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
+| Semantic Version | Invalid format | Set to `"1.0.0"` |
+| Concise Summary | Too long | Shorten to <200 chars |
+| Rich Description | Single line | Add multi-line explanation |
+| Complete Steps | Missing fields | Add required fields |
+| Unique Step IDs | Duplicate ID | Rename to unique identifier |
+| Valid Dependencies | Non-existent step | Fix step ID reference |
+| Input Consistency | from_step not in deps | Add step to dependencies |
+| Output Paths | Invalid format | Use valid filename/path |
+
+### Step 4: Re-Evaluate (If Needed)
+
+If any criteria failed:
+1. Review the updated workflow.yml
+2. Re-evaluate all criteria
+3. Fix remaining issues
+4. Repeat until all pass
+
+### Step 5: Confirm Completion
+
+When all criteria pass:
+
+1. Announce: "All workflow spec quality criteria pass."
+2. List what was validated
+3. Guide to next step: "Run `/experts.implement` to generate the step instruction files."
+
+## Output
+
+The validated `workflow.yml` file at `.deepwork/experts/[expert_name]/workflows/[workflow_name]/workflow.yml` that passes all quality criteria.
diff --git a/src/deepwork/standard/experts/experts/workflows/new_workflow/workflow.yml b/src/deepwork/standard/experts/experts/workflows/new_workflow/workflow.yml
new file mode 100644
index 00000000..0121ba2a
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/new_workflow/workflow.yml
@@ -0,0 +1,58 @@
+name: new_workflow
+version: "1.0.0"
+summary: "Create a new multi-step DeepWork workflow with spec definition, review, and implementation"
+
+description: |
+ Guide the user through creating a new DeepWork workflow by:
+ 1. Understanding their workflow requirements through interactive questioning
+ 2. Creating a validated workflow.yml specification
+ 3. Reviewing the spec against quality criteria
+ 4. Generating step instruction files and syncing skills
+
+steps:
+ - id: define
+ name: "Define Workflow Specification"
+ description: "Create a workflow.yml by gathering requirements through structured questions"
+ instructions_file: steps/define.md
+ inputs:
+ - name: workflow_purpose
+ description: "What complex task are you trying to accomplish?"
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ exposed: true
+
+ - id: review_workflow_spec
+ name: "Review Workflow Specification"
+ description: "Review workflow.yml against quality criteria using a sub-agent for unbiased validation"
+ instructions_file: steps/review_workflow_spec.md
+ inputs:
+ - file: ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ from_step: define
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ dependencies:
+ - define
+ agent: general-purpose
+ quality_criteria:
+ - "Workflow name is lowercase with underscores, no spaces or special characters"
+ - "Version follows X.Y.Z format (e.g., 1.0.0)"
+ - "Summary is under 200 characters and clearly describes the workflow"
+ - "Description is multi-line and explains problem, process, outcomes, and users"
+ - "Each step has id, name, description, instructions_file, outputs"
+ - "Step IDs are unique within the expert (across all workflows)"
+ - "Dependencies reference existing step IDs with no circular references"
+ - "File inputs with from_step reference a step in the dependencies array"
+ - "Outputs are valid filenames or paths"
+
+ - id: implement
+ name: "Implement Workflow Steps"
+ description: "Generate step instruction files and sync skills from the validated workflow.yml"
+ instructions_file: steps/implement.md
+ inputs:
+ - file: ".deepwork/experts/{expert_name}/workflows/{workflow_name}/workflow.yml"
+ from_step: review_workflow_spec
+ outputs:
+ - ".deepwork/experts/{expert_name}/workflows/{workflow_name}/steps/"
+ dependencies:
+ - review_workflow_spec
+ exposed: true
diff --git a/src/deepwork/standard/experts/experts/workflows/review_pr/steps/check_relevance.md b/src/deepwork/standard/experts/experts/workflows/review_pr/steps/check_relevance.md
new file mode 100644
index 00000000..d9dea491
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/review_pr/steps/check_relevance.md
@@ -0,0 +1,121 @@
+# Check Expert Relevance
+
+## Objective
+
+Invoke all available experts in parallel to determine which ones have relevant expertise for the PR changes. This filtering step ensures only applicable experts spend time on detailed review.
+
+## Task
+
+Assess which domain experts can meaningfully contribute to reviewing the current PR by having each expert examine the changes and report their relevance.
+
+### Process
+
+1. **Get PR metadata**
+
+ Get basic PR info for the output file:
+ ```bash
+ gh pr view --json number,title,headRefName
+ ```
+
+ This is needed for the output file header. The diff and file list will be
+ embedded directly in expert prompts using inline command expansion.
+
+2. **Discover available experts**
+
+ List all experts in the project:
+ ```bash
+ ls -1 .deepwork/experts/
+ ```
+
+ Read each expert's `discovery_description` from their `expert.yml` to understand their domain.
+
+3. **Invoke experts in parallel**
+
+ For each expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Use `$(command)` syntax in the prompt to embed command output directly when
+ the sub-agent is spawned. This avoids reading large diffs into the main
+ conversation context.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Review this PR to determine if the changes are relevant to your domain of expertise.
+
+ ## Changed Files
+
+ $(gh pr diff --name-only)
+
+ ## Diff Summary (first 200 lines)
+
+ $(gh pr diff | head -200)
+
+ ## Your Task
+
+ Based on your specific domain knowledge, respond with:
+ 1. RELEVANT or NOT_RELEVANT
+ 2. Brief justification (1-2 sentences) explaining why this does or does not fall within your expertise
+ 3. If relevant, which specific files/changes you can review from your expert perspective
+ ```
+
+ **Important**: Invoke ALL experts in parallel to minimize latency.
+
+4. **Collect and summarize responses**
+
+ Wait for all expert responses. Compile into the output file.
+
+## Output Format
+
+### pr_review/relevance_assessments.md
+
+Create this file with the assessment results:
+
+```markdown
+# PR Relevance Assessment
+
+**PR**: #[number] - [title]
+**Branch**: [branch_name]
+**Date**: [YYYY-MM-DD]
+
+## Changed Files
+
+- path/to/file1.py
+- path/to/file2.ts
+- ...
+
+## Expert Assessments
+
+### [expert-name-1]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+### [expert-name-2]
+**Status**: RELEVANT / NOT_RELEVANT
+**Justification**: [expert's reasoning]
+**Relevant files**: [if applicable]
+
+...
+
+## Summary
+
+**Relevant experts**: [list of expert names marked RELEVANT]
+**Next step**: Run `/experts.deep_review` with these experts
+```
+
+## Quality Criteria
+
+- All experts in `.deepwork/experts/` were invoked in parallel
+- Each expert provided a yes/no relevance determination with justification
+- Relevant experts are clearly identified for the next step
+- PR metadata (number, title, branch) is captured
+- Changed files are listed for reference
+
+## Context
+
+This is the first phase of the expert-driven PR review workflow. By checking relevance first, we avoid wasting expert review time on changes outside their domain. The parallel invocation ensures this filtering step completes quickly even with many experts.
+
+The output file serves as input to the `deep_review` step, which will only invoke experts marked as RELEVANT.
diff --git a/src/deepwork/standard/experts/experts/workflows/review_pr/steps/deep_review.md b/src/deepwork/standard/experts/experts/workflows/review_pr/steps/deep_review.md
new file mode 100644
index 00000000..01544707
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/review_pr/steps/deep_review.md
@@ -0,0 +1,192 @@
+# Deep Expert Review
+
+## Objective
+
+Have relevant domain experts perform thorough code review of the PR changes **specifically within their area of expertise**. Each expert produces detailed written feedback with specific suggestions for improvement, drawing on their domain knowledge.
+
+## Task
+
+Invoke only the experts marked as RELEVANT in the previous step to perform detailed code review, producing actionable feedback focused on their specific domain.
+
+### Process
+
+1. **Read the relevance assessments**
+
+ Read `pr_review/relevance_assessments.md` to identify:
+ - Which experts were marked RELEVANT
+ - Which specific files each expert identified as relevant to their domain
+ - The PR number and branch
+
+2. **Invoke relevant experts for deep review**
+
+ For each RELEVANT expert, use the Task tool to spawn a sub-agent:
+ - `subagent_type`: "experts"
+ - `prompt`: Use the template below with inline command expansion
+
+ **IMPORTANT - Use inline bash completion for efficiency**:
+
+ Instead of reading the diff yourself and passing it to each expert, use `$(command)`
+ syntax in the prompt. This embeds the command output directly when the sub-agent
+ is spawned, avoiding token overhead in the main conversation.
+
+ **Expert prompt template** (note the `$(...)` syntax):
+ ```
+ Perform a detailed code review of these PR changes **focusing specifically on your domain of expertise**.
+
+ IMPORTANT: Only comment on aspects that fall within your area of expertise. Do not provide
+ general code review feedback on things outside your domain - other experts will cover those areas.
+ Use your specialized knowledge to identify issues that a generalist reviewer might miss.
+
+ Your domain: [brief description from discovery_description]
+
+ Files you identified as relevant to your domain:
+ [list from relevance assessment]
+
+ ## PR Diff
+
+ $(gh pr diff)
+
+ ## Your Task
+
+ Review the diff above, focusing ONLY on files and changes relevant to your domain.
+ Ignore changes outside your expertise - other experts will cover those.
+
+ From your expert perspective, provide your review in this format:
+
+ ## Summary
+ Overall assessment of the changes as they relate to your domain (1-2 paragraphs).
+ What is this PR doing in terms of your area of expertise?
+
+ ## Issues Found
+ For each issue within your domain:
+ - **File**: path/to/file
+ - **Line(s)**: [line numbers]
+ - **Severity**: Critical / Major / Minor / Suggestion
+ - **Issue**: Description of the problem from your expert perspective
+ - **Suggestion**: How to fix it, drawing on your domain knowledge
+
+ ## Code Suggestions
+ Specific code changes you recommend based on your expertise (include before/after snippets).
+ Explain why this change is better from your domain's perspective.
+
+ ## Approval Status
+ - APPROVED: No blocking issues within your domain
+ - CHANGES_REQUESTED: Blocking issues in your domain must be addressed
+ - COMMENTS: Suggestions only within your domain, no blockers
+ ```
+
+ **Note**: Invoke relevant experts in parallel for efficiency.
+
+3. **Collect expert reviews**
+
+ Wait for all expert reviews to complete. Save each to their dedicated review file.
+
+4. **Create consolidated summary**
+
+ After all reviews complete, summarize across experts:
+ - Total issues by severity
+ - Key themes across reviews
+ - Overall approval status
+
+## Output Format
+
+### pr_review/[expert_name]/review.md
+
+Create one file per relevant expert:
+
+```markdown
+# [Expert Name] Review
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+**Reviewer**: [expert-name] expert
+
+## Summary
+
+[Expert's overall assessment from their domain perspective]
+
+## Issues Found
+
+### Issue 1
+- **File**: path/to/file.py
+- **Line(s)**: 45-52
+- **Severity**: Major
+- **Issue**: [Description from expert perspective]
+- **Suggestion**: [How to fix, using domain knowledge]
+
+### Issue 2
+...
+
+## Code Suggestions
+
+### Suggestion 1: [Brief title]
+
+**File**: path/to/file.py
+
+Before:
+```python
+# problematic code
+```
+
+After:
+```python
+# suggested improvement
+```
+
+**Rationale**: [Why this change improves the code from this expert's perspective]
+
+...
+
+## Approval Status
+
+[APPROVED / CHANGES_REQUESTED / COMMENTS]
+
+[Additional notes on approval status]
+```
+
+### pr_review/review_summary.md (optional but recommended)
+
+```markdown
+# PR Review Summary
+
+**PR**: #[number]
+**Date**: [YYYY-MM-DD]
+
+## Expert Reviews
+
+| Expert | Domain | Status | Critical | Major | Minor |
+|--------|--------|--------|----------|-------|-------|
+| [name] | [domain] | CHANGES_REQUESTED | 0 | 2 | 3 |
+| [name] | [domain] | APPROVED | 0 | 0 | 1 |
+
+## Key Themes
+
+- [Theme 1 appearing across multiple reviews]
+- [Theme 2]
+
+## Overall Status
+
+[CHANGES_REQUESTED if any expert requested changes, otherwise APPROVED]
+
+## Next Steps
+
+[If changes requested]: Run `/experts.improve_and_rereview` to address feedback
+[If approved]: PR is ready to merge
+```
+
+## Quality Criteria
+
+- Only experts marked as relevant were invoked
+- Each expert produced written feedback in their review file
+- Feedback is focused on each expert's specific domain of expertise
+- Specific code change suggestions are included where applicable
+- Issues include file, line numbers, severity, and suggestions
+- Approval status is clearly stated by each expert
+
+## Context
+
+This is the second phase of the expert-driven PR review workflow. Deep review only happens after relevance filtering to ensure expert time is well-spent. Each expert reviews independently from their unique domain perspective, bringing specialized knowledge to identify issues that generalist reviewers might miss.
+
+The key to effective expert review is **focus**: each expert should only comment on aspects within their domain, trusting that other experts will cover other areas. This produces higher-quality, more actionable feedback than generic reviews.
+
+The review files serve as input to the `improve_and_rereview` step if changes are requested. If all experts approve, the PR can proceed to merge.
diff --git a/src/deepwork/standard/experts/experts/workflows/review_pr/steps/improve_and_rereview.md b/src/deepwork/standard/experts/experts/workflows/review_pr/steps/improve_and_rereview.md
new file mode 100644
index 00000000..7218e174
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/review_pr/steps/improve_and_rereview.md
@@ -0,0 +1,226 @@
+# Improve and Re-Review
+
+## Objective
+
+Iteratively improve the PR based on expert feedback, then re-run expert reviews until all feedback is addressed or a maximum of 3 iterations is reached.
+
+## Task
+
+Apply improvements based on expert review feedback, get user approval, then have the same experts re-review. Repeat until experts report no further issues.
+
+### Process
+
+1. **Read expert reviews**
+
+ Read all review files from `pr_review/[expert_name]/review.md` to understand:
+ - What issues each expert identified within their domain
+ - What code changes they suggested
+ - Their approval status
+
+2. **Consolidate and prioritize feedback**
+
+ Group feedback by:
+ - **Critical/Major issues**: Must be addressed
+ - **Minor issues**: Should be addressed
+ - **Suggestions**: Nice to have
+
+ Present a summary to the user:
+ ```
+ ## Feedback Summary
+
+ ### Critical/Major Issues (must fix)
+ 1. [Issue from expert X]: [description]
+ 2. [Issue from expert Y]: [description]
+
+ ### Minor Issues (should fix)
+ 1. [Issue]: [description]
+
+ ### Suggestions (optional)
+ 1. [Suggestion]: [description]
+
+ ## Proposed Changes
+
+ I recommend making these changes:
+ 1. [Change 1]: [what and why]
+ 2. [Change 2]: [what and why]
+
+ Do you approve these changes?
+ ```
+
+3. **Get user approval**
+
+ **IMPORTANT**: Do not make changes without explicit user approval.
+
+ Wait for user to:
+ - Approve the proposed changes
+ - Modify which changes to apply
+ - Skip certain suggestions with justification
+
+4. **Apply approved changes**
+
+ Make the code changes the user approved:
+ - Use the Edit tool to modify files
+ - Follow the expert's code suggestions where applicable
+ - Ensure changes are coherent and don't break other code
+
+5. **Re-run expert reviews**
+
+ After applying changes, invoke the same relevant experts again:
+ - Use the Task tool with `subagent_type`: "experts"
+ - Provide the updated file contents and diff
+ - Ask them to re-review from their domain perspective
+
+ **Expert re-review prompt**:
+ ```
+ This is a re-review of PR changes after addressing your previous feedback.
+
+ Your domain: [brief description from discovery_description]
+
+ Previous issues you raised within your domain:
+ [list their issues from last review]
+
+ Changes made:
+ [summary of changes applied]
+
+ Updated files:
+ [full file contents]
+
+ From your expert perspective, please review and report:
+ 1. Are your previous domain-specific issues addressed?
+ 2. Any new issues introduced within your domain?
+ 3. Updated approval status for your domain
+ ```
+
+6. **Evaluate results**
+
+ After re-review:
+ - If ALL experts now approve (or only have minor suggestions): **Stop - review complete**
+ - If any expert still has Critical/Major issues: **Continue to next iteration**
+ - If this was iteration 3: **Stop - maximum iterations reached**
+
+7. **Create iteration summary**
+
+ Document what happened in this iteration.
+
+### Iteration Loop
+
+Repeat steps 2-7 until:
+- All experts report no further blocking feedback, OR
+- 3 iterations have been completed
+
+## Output Format
+
+### pr_review/iteration_[n]/summary.md
+
+Create one file per iteration:
+
+```markdown
+# Iteration [n] Summary
+
+**Date**: [YYYY-MM-DD]
+**Iteration**: [n] of 3 max
+
+## Feedback Addressed
+
+**IMPORTANT**: List ALL issues from expert reviews - none may be silently omitted.
+
+### From [expert-name]
+| Issue | Severity | Status | Resolution |
+|-------|----------|--------|------------|
+| [Issue 1] | Major | FIXED | [How it was fixed] |
+| [Issue 2] | Minor | SKIPPED | [Explicit reason: false positive / deferred / user declined] |
+| [Issue 3] | Suggestion | FIXED | [How it was fixed] |
+
+### From [expert-name-2]
+...
+
+## Changes Made
+
+1. **[file.py]**: [What was changed]
+2. **[other.ts]**: [What was changed]
+
+## Re-Review Results
+
+| Expert | Previous Status | New Status | Remaining Issues |
+|--------|-----------------|------------|------------------|
+| [name] | CHANGES_REQUESTED | APPROVED | 0 |
+| [name] | CHANGES_REQUESTED | COMMENTS | 1 minor |
+
+## Outcome
+
+[One of:]
+- **COMPLETE**: All experts approved. PR ready to merge.
+- **CONTINUING**: [n] blocking issues remain. Proceeding to iteration [n+1].
+- **MAX_ITERATIONS**: Reached 3 iterations. [n] issues remain unresolved.
+
+## Remaining Issues (if any)
+
+1. [Issue]: [Why not addressed / Plan for addressing]
+```
+
+### Final Summary (after last iteration)
+
+Update or create `pr_review/final_summary.md`:
+
+```markdown
+# PR Review Final Summary
+
+**PR**: #[number]
+**Total Iterations**: [n]
+**Final Status**: [APPROVED / PARTIAL / UNRESOLVED]
+
+## Review Timeline
+
+| Iteration | Date | Issues Addressed | Remaining |
+|-----------|------|------------------|-----------|
+| 1 | [date] | 5 | 3 |
+| 2 | [date] | 3 | 0 |
+
+## Expert Final Status
+
+| Expert | Final Status |
+|--------|--------------|
+| [name] | APPROVED |
+| [name] | APPROVED |
+
+## Key Improvements Made
+
+1. [Improvement 1]
+2. [Improvement 2]
+
+## Issue Resolution Summary
+
+| Expert | Total Issues | Fixed | Skipped (with reason) |
+|--------|--------------|-------|----------------------|
+| [name] | 4 | 3 | 1 (false positive) |
+| [name] | 2 | 2 | 0 |
+
+## Unresolved Items (if any)
+
+For each unresolved item, document:
+- Issue description
+- Why it wasn't fixed
+- Plan for addressing (if applicable)
+
+## Next Steps
+
+[What to do with the PR now]
+```
+
+## Quality Criteria
+
+- **Complete issue accounting**: Every issue from expert reviews is either:
+ - Fixed (with description of the fix), OR
+ - Explicitly skipped with documented justification (e.g., "false positive", "deferred to future PR", "user declined")
+- **No silent omissions**: The feedback summary must list ALL issues from expert reviews, not a subset
+- User approved suggested improvements before they were applied
+- Re-reviews were run after each improvement iteration
+- Cycle stopped when all experts reported no further feedback OR after 3 iterations
+- Final iteration summary documents the outcome for every issue
+- All changes are tracked and attributed to expert feedback
+
+## Context
+
+This is the final phase of the expert-driven PR review workflow. The iterative approach ensures feedback is actually addressed, not just acknowledged. The 3-iteration limit prevents infinite loops while allowing reasonable time for improvements.
+
+User approval at each step keeps humans in control - experts suggest, but humans decide what changes to make. This respects developer autonomy while benefiting from expert review.
diff --git a/src/deepwork/standard/experts/experts/workflows/review_pr/workflow.yml b/src/deepwork/standard/experts/experts/workflows/review_pr/workflow.yml
new file mode 100644
index 00000000..2c5455da
--- /dev/null
+++ b/src/deepwork/standard/experts/experts/workflows/review_pr/workflow.yml
@@ -0,0 +1,44 @@
+name: review_pr
+version: "1.0.0"
+summary: "Coordinate expert-driven PR review with iterative improvement cycles"
+
+description: |
+ Orchestrate a comprehensive PR review using domain experts:
+ 1. Check which experts have relevant expertise for the PR changes
+ 2. Have relevant experts perform deep code review
+ 3. Iteratively improve the PR based on feedback until all experts approve
+
+steps:
+ - id: check_relevance
+ name: "Check Expert Relevance"
+ description: "Invoke all experts in parallel to assess if PR changes are relevant to their domain"
+ instructions_file: steps/check_relevance.md
+ outputs:
+ - "pr_review/relevance_assessments.md"
+ exposed: true
+
+ - id: deep_review
+ name: "Deep Expert Review"
+ description: "Invoke only relevant experts to perform detailed code review"
+ instructions_file: steps/deep_review.md
+ inputs:
+ - file: "pr_review/relevance_assessments.md"
+ from_step: check_relevance
+ outputs:
+ - "pr_review/{expert_name}/review.md"
+ - "pr_review/review_summary.md"
+ dependencies:
+ - check_relevance
+
+ - id: improve_and_rereview
+ name: "Improve and Re-Review"
+ description: "Apply expert feedback and re-run reviews until no further feedback or 3 iterations"
+ instructions_file: steps/improve_and_rereview.md
+ inputs:
+ - file: "pr_review/{expert_name}/review.md"
+ from_step: deep_review
+ outputs:
+ - "pr_review/iteration_{n}/summary.md"
+ - "pr_review/final_summary.md"
+ dependencies:
+ - deep_review
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/AGENTS.md b/src/deepwork/standard_jobs/deepwork_jobs/AGENTS.md
deleted file mode 100644
index 6d97d0e5..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/AGENTS.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Project Context for deepwork_jobs
-
-This is the source of truth for the `deepwork_jobs` standard job.
-
-## Codebase Structure
-
-- Source location: `src/deepwork/standard_jobs/deepwork_jobs/`
-- Working copy: `.deepwork/jobs/deepwork_jobs/`
-- Templates: `templates/` directory within each location
-
-## Dual Location Maintenance
-
-**Important**: This job exists in two locations that must be kept in sync:
-
-1. **Source of truth**: `src/deepwork/standard_jobs/deepwork_jobs/`
- - This is where changes should be made first
- - Tracked in version control
-
-2. **Working copy**: `.deepwork/jobs/deepwork_jobs/`
- - Must be updated after changes to source
- - Used by `deepwork sync` to generate commands
-
-After making changes to the source, copy files to the working copy:
-```bash
-cp src/deepwork/standard_jobs/deepwork_jobs/job.yml .deepwork/jobs/deepwork_jobs/
-cp src/deepwork/standard_jobs/deepwork_jobs/steps/*.md .deepwork/jobs/deepwork_jobs/steps/
-cp -r src/deepwork/standard_jobs/deepwork_jobs/templates/* .deepwork/jobs/deepwork_jobs/templates/
-```
-
-## File Organization
-
-```
-deepwork_jobs/
-├── AGENTS.md # This file
-├── job.yml # Job definition
-├── make_new_job.sh # Script to create new job structure
-├── steps/
-│ ├── define.md # Define step instructions
-│ ├── implement.md # Implement step instructions
-│ ├── learn.md # Learn step instructions
-│ └── supplemental_file_references.md # Reference documentation
-└── templates/
- ├── job.yml.template # Job spec structure
- ├── step_instruction.md.template # Step instruction structure
- ├── agents.md.template # AGENTS.md structure
- ├── job.yml.example # Complete job example
- └── step_instruction.md.example # Complete step example
-```
-
-## Version Management
-
-- Version is tracked in `job.yml`
-- Bump patch version (0.0.x) for instruction improvements
-- Bump minor version (0.x.0) for new features or structural changes
-- Always update changelog when bumping version
-
-## Last Updated
-
-- Date: 2026-01-15
-- From conversation about: Adding make_new_job.sh script and templates directory
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/doc_specs/job_spec.md b/src/deepwork/standard_jobs/deepwork_jobs/doc_specs/job_spec.md
deleted file mode 100644
index b880bb17..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/doc_specs/job_spec.md
+++ /dev/null
@@ -1,203 +0,0 @@
----
-name: "DeepWork Job Specification"
-description: "YAML specification file that defines a multi-step workflow job for AI agents"
-path_patterns:
- - ".deepwork/jobs/*/job.yml"
-target_audience: "AI agents executing jobs and developers defining workflows"
-frequency: "Created once per job, updated as workflow evolves"
-quality_criteria:
- - name: Valid Identifier
- description: "Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)"
- - name: Semantic Version
- description: "Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)"
- - name: Concise Summary
- description: "Summary must be under 200 characters and clearly describe what the job accomplishes"
- - name: Rich Description
- description: "Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users"
- - name: Changelog Present
- description: "Must include a changelog array with at least the initial version entry. Changelog should only include one entry per branch at most"
- - name: Complete Steps
- description: "Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array"
- - name: Valid Dependencies
- description: "Dependencies must reference existing step IDs with no circular references"
- - name: Input Consistency
- description: "File inputs with `from_step` must reference a step that is in the dependencies array"
- - name: Output Paths
- description: "Outputs must be valid filenames or paths within the main repo directory structure, never in dot-directories like `.deepwork/`. Use specific, descriptive paths that lend themselves to glob patterns (e.g., `competitive_research/acme_corp/swot.md` or `operations/reports/2026-01/spending_analysis.md`). Parameterized paths like `[competitor_name]/` are encouraged for per-entity outputs. Avoid generic names (`output.md`, `analysis.md`) and transient-sounding paths (`temp/`, `draft.md`). Supporting materials for a final output should go in a peer `_dataroom` folder (e.g., `spending_analysis_dataroom/`)."
- - name: Concise Instructions
- description: "The content of the file, particularly the description, must not have excessively redundant information. It should be concise and to the point given that extra tokens will confuse the AI."
----
-
-# DeepWork Job Specification: [job_name]
-
-A `job.yml` file defines a complete multi-step workflow that AI agents can execute. Each job breaks down a complex task into reviewable steps with clear inputs and outputs.
-
-## Required Fields
-
-### Top-Level Metadata
-
-```yaml
-name: job_name # lowercase, underscores only
-version: "1.0.0" # semantic versioning
-summary: "Brief description" # max 200 characters
-description: | # detailed multi-line explanation
- [Explain what this workflow does, why it exists,
- what outputs it produces, and who should use it]
-```
-
-### Changelog
-
-```yaml
-changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
- - version: "1.1.0"
- changes: "Added quality validation hooks"
-```
-
-### Steps Array
-
-```yaml
-steps:
- - id: step_id # unique, lowercase_underscores
- name: "Human Readable Name"
- description: "What this step accomplishes"
- instructions_file: steps/step_id.md
- inputs:
- # User-provided inputs:
- - name: param_name
- description: "What the user provides"
- # File inputs from previous steps:
- - file: output.md
- from_step: previous_step_id
- outputs:
- - competitive_research/competitors_list.md # descriptive path
- - competitive_research/[competitor_name]/research.md # parameterized path
- # With doc spec reference:
- - file: competitive_research/final_report.md
- doc_spec: .deepwork/doc_specs/report_type.md
- dependencies:
- - previous_step_id # steps that must complete first
-```
-
-## Optional Fields
-
-### Exposed Steps
-
-```yaml
-steps:
- - id: learn
- exposed: true # Makes step available without running dependencies
-```
-
-### Agent Delegation
-
-When a step should be executed by a specific agent type, use the `agent` field. This automatically sets `context: fork` in the generated skill.
-
-```yaml
-steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
-```
-
-Available agent types:
-- `general-purpose` - Standard agent for multi-step tasks
-
-### Quality Hooks
-
-```yaml
-steps:
- - id: step_id
- hooks:
- after_agent:
- # Inline prompt for quality validation:
- - prompt: |
- Verify the output meets criteria:
- 1. [Criterion 1]
- 2. [Criterion 2]
- If ALL criteria are met, include `...`.
- # External prompt file:
- - prompt_file: hooks/quality_check.md
- # Script for programmatic validation:
- - script: hooks/run_tests.sh
-```
-
-### Stop Hooks (Legacy)
-
-```yaml
-steps:
- - id: step_id
- stop_hooks:
- - prompt: "Validation prompt..."
- - prompt_file: hooks/check.md
- - script: hooks/validate.sh
-```
-
-## Validation Rules
-
-1. **No circular dependencies**: Step A cannot depend on Step B if Step B depends on Step A
-2. **File inputs require dependencies**: If a step uses `from_step: X`, then X must be in its dependencies
-3. **Unique step IDs**: No two steps can have the same id
-4. **Valid file paths**: Output paths must not contain invalid characters and should be in the main repo (not dot-directories)
-5. **Instructions files exist**: Each `instructions_file` path should have a corresponding file created
-
-## Example: Complete Job Specification
-
-```yaml
-name: competitive_research
-version: "1.0.0"
-summary: "Systematic competitive analysis workflow"
-description: |
- A comprehensive workflow for analyzing competitors in your market segment.
- Helps product teams understand the competitive landscape through systematic
- identification, research, comparison, and positioning recommendations.
-
- Produces:
- - Vetted competitor list
- - Research notes per competitor
- - Comparison matrix
- - Strategic positioning report
-
-changelog:
- - version: "1.0.0"
- changes: "Initial job creation"
-
-steps:
- - id: identify_competitors
- name: "Identify Competitors"
- description: "Identify 5-7 key competitors in the target market"
- instructions_file: steps/identify_competitors.md
- inputs:
- - name: market_segment
- description: "The market segment to analyze"
- - name: product_category
- description: "The product category"
- outputs:
- - competitive_research/competitors_list.md
- dependencies: []
-
- - id: research_competitors
- name: "Research Competitors"
- description: "Deep dive research on each identified competitor"
- instructions_file: steps/research_competitors.md
- inputs:
- - file: competitive_research/competitors_list.md
- from_step: identify_competitors
- outputs:
- - competitive_research/[competitor_name]/research.md
- dependencies:
- - identify_competitors
-
- - id: positioning_report
- name: "Positioning Report"
- description: "Strategic positioning recommendations"
- instructions_file: steps/positioning_report.md
- inputs:
- - file: competitive_research/[competitor_name]/research.md
- from_step: research_competitors
- outputs:
- - file: competitive_research/positioning_report.md
- doc_spec: .deepwork/doc_specs/positioning_report.md
- dependencies:
- - research_competitors
-```
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/job.yml b/src/deepwork/standard_jobs/deepwork_jobs/job.yml
deleted file mode 100644
index 4343cbda..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/job.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-name: deepwork_jobs
-version: "1.0.0"
-summary: "Creates and manages multi-step AI workflows. Use when defining, implementing, or improving DeepWork jobs."
-description: |
- Core commands for managing DeepWork jobs. These commands help you define new multi-step
- workflows and learn from running them.
-
- The `new_job` workflow guides you through defining and implementing a new job by
- asking structured questions about your workflow, understanding each step's inputs and outputs,
- reviewing the specification, and generating all necessary files.
-
- The `learn` skill reflects on conversations where DeepWork jobs were run, identifies
- confusion or inefficiencies, and improves job instructions. It also captures bespoke
- learnings specific to the current run into AGENTS.md files in the working folder.
-
-workflows:
- - name: new_job
- summary: "Create a new DeepWork job from scratch through definition, review, and implementation"
- steps:
- - define
- - review_job_spec
- - implement
-
-changelog:
- - version: "1.0.0"
- changes: "Added workflows section to distinguish new_job workflow (define→review_job_spec→implement) from standalone learn skill"
- - version: "0.1.0"
- changes: "Initial version"
- - version: "0.2.0"
- changes: "Replaced refine command with learn command for conversation-driven improvement"
- - version: "0.3.0"
- changes: "Added make_new_job.sh script and templates directory; updated instructions to reference templates instead of inline examples"
- - version: "0.4.0"
- changes: "Removed implementation_summary and learning_summary outputs; simplified step outputs"
- - version: "0.5.0"
- changes: "Standardized on 'ask structured questions' phrasing for user input; Updated quality criteria hooks to verify phrase usage; Added guidance in implement.md to use phrase in generated instructions"
- - version: "0.6.0"
- changes: "Added doc spec support; define.md now detects document-oriented workflows and guides doc spec creation; learn.md now identifies and applies doc spec-related improvements"
- - version: "0.7.0"
- changes: "Added job.yml doc spec; define step now outputs job.yml with doc_spec reference for quality validation"
- - version: "0.8.0"
- changes: "Added review_job_spec step between define and implement for doc spec-based quality validation using sub-agent review"
- - version: "0.9.0"
- changes: "Improved skill descriptions with third-person voice and 'Use when...' triggers for better discoverability"
-
-steps:
- - id: define
- name: "Define Job Specification"
- description: "Creates a job.yml specification by gathering workflow requirements through structured questions. Use when starting a new multi-step workflow."
- instructions_file: steps/define.md
- inputs:
- - name: job_purpose
- description: "What complex task or workflow are you trying to accomplish?"
- outputs:
- - file: job.yml
- doc_spec: .deepwork/doc_specs/job_spec.md
- dependencies: []
- - id: review_job_spec
- name: "Review Job Specification"
- description: "Reviews job.yml against quality criteria using a sub-agent for unbiased validation. Use after defining a job specification."
- instructions_file: steps/review_job_spec.md
- inputs:
- - file: job.yml
- from_step: define
- outputs:
- - file: job.yml
- doc_spec: .deepwork/doc_specs/job_spec.md
- dependencies:
- - define
- quality_criteria:
- - "**Sub-Agent Used**: Was a sub-agent spawned to provide unbiased review?"
- - "**All doc spec Criteria Evaluated**: Did the sub-agent assess all 9 quality criteria?"
- - "**Findings Addressed**: Were all failed criteria addressed by the main agent?"
- - "**Validation Loop Complete**: Did the review-fix cycle continue until all criteria passed?"
-
- - id: implement
- name: "Implement Job Steps"
- description: "Generates step instruction files and syncs slash commands from the job.yml specification. Use after job spec review passes."
- instructions_file: steps/implement.md
- inputs:
- - file: job.yml
- from_step: review_job_spec
- outputs:
- - steps/
- dependencies:
- - review_job_spec
- quality_criteria:
- - "**Directory Structure**: Is `.deepwork/jobs/[job_name]/` created correctly?"
- - "**Complete Instructions**: Are ALL step instruction files complete (not stubs or placeholders)?"
- - "**Specific & Actionable**: Are instructions tailored to each step's purpose, not generic?"
- - "**Output Examples**: Does each instruction file show what good output looks like?"
- - "**Quality Criteria**: Does each instruction file define quality criteria for its outputs?"
- - "**Ask Structured Questions**: Do step instructions that gather user input explicitly use the phrase \"ask structured questions\"?"
- - "**Sync Complete**: Has `deepwork sync` been run successfully?"
- - "**Commands Available**: Are the slash-commands generated in `.claude/commands/`?"
- - "**Rules Considered**: Has the agent thought about whether rules would benefit this job? If relevant rules were identified, did they explain them and offer to run `/deepwork_rules.define`? Not every job needs rules - only suggest when genuinely helpful."
-
- - id: learn
- name: "Learn from Job Execution"
- description: "Analyzes conversation history to improve job instructions and capture learnings. Use after running a job to refine it."
- instructions_file: steps/learn.md
- exposed: true
- inputs:
- - name: job_name
- description: "Name of the job that was run (optional - will auto-detect from conversation)"
- outputs:
- - AGENTS.md
- dependencies: []
- quality_criteria:
- - "**Conversation Analyzed**: Did the agent review the conversation for DeepWork job executions?"
- - "**Confusion Identified**: Did the agent identify points of confusion, errors, or inefficiencies?"
- - "**Instructions Improved**: Were job instructions updated to address identified issues?"
- - "**Instructions Concise**: Are instructions free of redundancy and unnecessary verbosity?"
- - "**Shared Content Extracted**: Is lengthy/duplicated content extracted into referenced files?"
- - "**doc spec Reviewed (if applicable)**: For jobs with doc spec outputs, were doc spec-related learnings identified?"
- - "**doc spec Updated (if applicable)**: Were doc spec files updated with improved quality criteria or structure?"
- - "**Bespoke Learnings Captured**: Were run-specific learnings added to AGENTS.md?"
- - "**File References Used**: Do AGENTS.md entries reference other files where appropriate?"
- - "**Working Folder Correct**: Is AGENTS.md in the correct working folder for the job?"
- - "**Generalizable Separated**: Are generalizable improvements in instructions, not AGENTS.md?"
- - "**Sync Complete**: Has `deepwork sync` been run if instructions were modified?"
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/make_new_job.sh b/src/deepwork/standard_jobs/deepwork_jobs/make_new_job.sh
deleted file mode 100755
index c561d6d2..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/make_new_job.sh
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env bash
-#
-# make_new_job.sh - Create directory structure for a new DeepWork job
-#
-# Usage: ./make_new_job.sh
-#
-
-set -euo pipefail
-
-# Color output helpers
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-NC='\033[0m' # No Color
-
-info() {
- echo -e "${GREEN}[INFO]${NC} $1"
-}
-
-warn() {
- echo -e "${YELLOW}[WARN]${NC} $1"
-}
-
-error() {
- echo -e "${RED}[ERROR]${NC} $1"
- exit 1
-}
-
-# Validate job name format
-validate_job_name() {
- local name="$1"
- if [[ ! "$name" =~ ^[a-z][a-z0-9_]*$ ]]; then
- error "Invalid job name '$name'. Must be lowercase, start with a letter, and contain only letters, numbers, and underscores."
- fi
-}
-
-# Main script
-main() {
- if [[ $# -lt 1 ]]; then
- echo "Usage: $0 "
- echo ""
- echo "Creates the directory structure for a new DeepWork job."
- echo ""
- echo "Arguments:"
- echo " job_name Name of the job (lowercase, underscores allowed)"
- echo ""
- echo "Example:"
- echo " $0 competitive_research"
- exit 1
- fi
-
- local job_name="$1"
- validate_job_name "$job_name"
-
- # Determine the base path - look for .deepwork directory
- local base_path
- if [[ -d ".deepwork/jobs" ]]; then
- base_path=".deepwork/jobs"
- elif [[ -d "../.deepwork/jobs" ]]; then
- base_path="../.deepwork/jobs"
- else
- # Create from current directory
- base_path=".deepwork/jobs"
- mkdir -p "$base_path"
- fi
-
- local job_path="${base_path}/${job_name}"
-
- # Check if job already exists
- if [[ -d "$job_path" ]]; then
- error "Job '$job_name' already exists at $job_path"
- fi
-
- info "Creating job directory structure for '$job_name'..."
-
- # Create main job directory and subdirectories
- mkdir -p "$job_path"
- mkdir -p "$job_path/steps"
- mkdir -p "$job_path/hooks"
- mkdir -p "$job_path/templates"
-
- # Add .gitkeep files to empty directories
- touch "$job_path/hooks/.gitkeep"
- touch "$job_path/templates/.gitkeep"
-
- # Create AGENTS.md file
- cat > "$job_path/AGENTS.md" << 'EOF'
-# Job Management
-
-This folder and its subfolders are managed using the `deepwork_jobs` slash commands.
-
-## Recommended Commands
-
-- `/deepwork_jobs.define` - Create or modify the job.yml specification
-- `/deepwork_jobs.implement` - Generate step instruction files from the specification
-- `/deepwork_jobs.learn` - Improve instructions based on execution learnings
-
-## Directory Structure
-
-```
-.
-├── AGENTS.md # This file - project context and guidance
-├── job.yml # Job specification (created by /deepwork_jobs.define)
-├── steps/ # Step instruction files (created by /deepwork_jobs.implement)
-│ └── *.md # One file per step
-├── hooks/ # Custom validation scripts and prompts
-│ └── *.md|*.sh # Hook files referenced in job.yml
-└── templates/ # Example file formats and templates
- └── *.md|*.yml # Templates referenced in step instructions
-```
-
-## Editing Guidelines
-
-1. **Use slash commands** for structural changes (adding steps, modifying job.yml)
-2. **Direct edits** are fine for minor instruction tweaks
-3. **Run `/deepwork_jobs.learn`** after executing job steps to capture improvements
-4. **Run `deepwork sync`** after any changes to regenerate commands
-EOF
-
- info "Created directory structure:"
- echo " $job_path/"
- echo " ├── AGENTS.md"
- echo " ├── steps/"
- echo " ├── hooks/.gitkeep"
- echo " └── templates/.gitkeep"
-
- echo ""
- info "Next steps:"
- echo " 1. Run '/deepwork_jobs.define' to create the job.yml specification"
- echo " 2. Run '/deepwork_jobs.implement' to generate step instructions"
- echo " 3. Run 'deepwork sync' to create slash commands"
-}
-
-main "$@"
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/define.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/define.md
deleted file mode 100644
index 31de7440..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/steps/define.md
+++ /dev/null
@@ -1,458 +0,0 @@
-# Define Job Specification
-
-## Objective
-
-Create a `job.yml` specification file that defines the structure of a new DeepWork job by thoroughly understanding the user's workflow requirements through an interactive question-and-answer process.
-
-## Task
-
-Guide the user through defining a job specification by asking structured questions. **Do not attempt to create the specification without first fully understanding the user's needs.**
-
-**Important**: Use the AskUserQuestion tool to ask structured questions when gathering information from the user. This provides a better user experience with clear options and guided choices.
-
-The output of this step is **only** the `job.yml` file - a complete specification of the workflow. The actual step instruction files will be created in the next step (`implement`).
-
-### Step 1: Understand the Job Purpose
-
-Start by asking structured questions to understand what the user wants to accomplish:
-
-1. **What is the overall goal of this workflow?**
- - What complex task are they trying to accomplish?
- - What domain is this in? (e.g., research, marketing, development, reporting)
- - How often will they run this workflow?
-
-2. **What does success look like?**
- - What's the final deliverable or outcome?
- - Who is the audience for the output?
- - What quality criteria matter most?
-
-3. **What are the major phases?**
- - Ask them to describe the workflow at a high level
- - What are the distinct stages from start to finish?
- - Are there any dependencies between phases?
-
-### Step 1.5: Detect Document-Oriented Workflows
-
-**Check for document-focused patterns** in the user's description:
-- Keywords: "report", "summary", "document", "create", "monthly", "quarterly", "for stakeholders", "for leadership"
-- Final deliverable is a specific document (e.g., "AWS spending report", "competitive analysis", "sprint summary")
-- Recurring documents with consistent structure
-
-**If a document-oriented workflow is detected:**
-
-1. Inform the user: "This workflow produces a specific document type. I recommend defining a doc spec first to ensure consistent quality."
-
-2. Ask structured questions to understand if they want to:
- - Create a doc spec for this document
- - Use an existing doc spec (if any exist in `.deepwork/doc_specs/`)
- - Skip doc spec and proceed with simple outputs
-
-### Step 1.6: Define the Doc Spec (if needed)
-
-When creating a doc spec, gather the following information:
-
-1. **Document Identity**
- - What is the document called? (e.g., "Monthly AWS Spending Report")
- - Brief description of its purpose
- - Where should these documents be stored? (path patterns like `finance/aws-reports/*.md`)
-
-2. **Audience and Context**
- - Who reads this document? (target audience)
- - How often is it produced? (frequency)
-
-3. **Quality Criteria** (3-5 criteria, each with name and description)
-
- **Important**: Doc spec quality criteria define requirements for the **output document itself**, not the process of creating it. Focus on what the finished document must contain or achieve.
-
- Examples for a spending report:
- - **Visualization**: Must include charts showing spend breakdown by service
- - **Variance Analysis**: Must compare current month against previous with percentages
- - **Action Items**: Must include recommended cost optimization actions
-
- **Note**: When a doc spec is created for a step's output, the step should generally NOT have separate `quality_criteria` in the job.yml. The doc spec's criteria cover output quality. Only add step-level quality_criteria if there are essential process requirements (e.g., "must use specific tool"), and minimize these when possible.
-
-4. **Document Structure**
- - What sections should it have?
- - Any required elements (tables, charts, summaries)?
-
-### Step 1.7: Create the doc spec File (if needed)
-
-Create the doc spec file at `.deepwork/doc_specs/[doc_spec_name].md`:
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/doc_spec.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/doc_specs/job_spec.md` for a fully worked example (the doc spec for job.yml files).
-
-After creating the doc spec, proceed to Step 2 with the doc spec reference for the final step's output.
-
-### Step 2: Define Each Step
-
-For each major phase they mentioned, ask structured questions to gather details:
-
-1. **Step Purpose**
- - What exactly does this step accomplish?
- - What is the input to this step?
- - What is the output from this step?
-
-2. **Step Inputs**
- - What information is needed to start this step?
- - Does it need user-provided parameters? (e.g., topic, target audience)
- - Does it need files from previous steps?
- - What format should inputs be in?
-
-3. **Step Outputs**
- - What files or artifacts does this step produce?
- - What format should the output be in? (markdown, YAML, JSON, etc.)
- - Where should each output be saved? (filename/path)
- - Should outputs be organized in subdirectories? (e.g., `reports/`, `data/`, `drafts/`)
- - Will other steps need this output?
- - **Does this output have a doc spec?** If a doc spec was created in Step 1.6/1.7, reference it for the appropriate output
-
- #### Work Product Storage Guidelines
-
- **Key principle**: Job outputs belong in the main repository directory structure, not in dot-directories. The `.deepwork/` directory is for job definitions and configuration only.
-
- **Why this matters**:
- - **Version control**: Work products in the main repo are tracked by git and visible in PRs
- - **Discoverability**: Team members can find outputs without knowing about DeepWork internals
- - **Tooling compatibility**: IDEs, search tools, and CI/CD work naturally with standard paths
- - **Glob patterns**: Well-structured paths enable powerful file matching (e.g., `competitive_research/**/*.md`)
-
- **Good output path patterns**:
- ```
- competitive_research/competitors_list.md
- competitive_research/acme_corp/research.md
- operations/reports/2026-01/spending_analysis.md
- docs/api/endpoints.md
- ```
-
- **Avoid these patterns**:
- ```
- .deepwork/outputs/report.md # Hidden in dot-directory
- output.md # Too generic, no context
- research.md # Unclear which research
- temp/draft.md # Transient-sounding paths
- ```
-
- **Organizing multi-file outputs**:
- - Use the job name as a top-level folder when outputs are job-specific
- - Use parameterized paths for per-entity outputs: `competitive_research/[competitor_name]/`
- - Match existing project conventions when extending a codebase
-
- **When to include dates in paths**:
- - **Include date** for periodic outputs where each version is retained (e.g., monthly reports, quarterly reviews, weekly summaries). These accumulate over time and historical versions remain useful.
- ```
- operations/reports/2026-01/spending_analysis.md # Monthly report - keep history
- hr/employees/[employee_name]/quarterly_reviews/2026-Q1.pdf # Per-employee quarterly review
- ```
- - **Omit date** for current-state outputs that represent the latest understanding and get updated in place. Previous versions live in git history, not separate files.
- ```
- competitive_research/acme_corp/swot.md # Current SWOT - updated over time
- docs/architecture/overview.md # Living document
- ```
-
- **Supporting materials and intermediate outputs**:
- - Content generated in earlier steps to support the final output (research notes, data extracts, drafts) should be placed in a `_dataroom` folder that is a peer to the final output
- - Name the dataroom folder by replacing the file extension with `_dataroom`
- ```
- operations/reports/2026-01/spending_analysis.md # Final output
- operations/reports/2026-01/spending_analysis_dataroom/ # Supporting materials
- raw_data.csv
- vendor_breakdown.md
- notes.md
- ```
- - This keeps supporting materials organized and discoverable without cluttering the main output location
-
-4. **Step Dependencies**
- - Which previous steps must complete before this one?
- - Are there any ordering constraints?
-
-5. **Step Process** (high-level understanding)
- - What are the key activities in this step?
- - Are there any quality checks or validation needed?
- - What makes a good vs. bad output for this step?
-
-6. **Agent Delegation** (optional)
- - Should this step be executed by a specific agent type?
- - Use the `agent` field when the step should run in a forked context with a specific agent
- - When `agent` is set, the generated skill automatically includes `context: fork`
- - Available agent types:
- - `general-purpose` - Standard agent for multi-step tasks
-
- ```yaml
- steps:
- - id: research_step
- agent: general-purpose # Delegates to the general-purpose agent
- ```
-
-**Note**: You're gathering this information to understand what instructions will be needed, but you won't create the instruction files yet - that happens in the `implement` step.
-
-#### Doc Spec-Aware Output Format
-
-When a step produces a document with a doc spec reference, use this format in job.yml:
-
-```yaml
-outputs:
- - file: reports/monthly_spending.md
- doc_spec: .deepwork/doc_specs/monthly_aws_report.md
-```
-
-The doc spec's quality criteria will automatically be included in the generated skill, ensuring consistent document quality.
-
-### Capability Considerations
-
-When defining steps, identify any that require specialized tools:
-
-**Browser Automation**: If any step involves web scraping, form filling, interactive browsing, UI testing, or research requiring website visits, ask the user what browser tools they have available. For Claude Code users, **Claude in Chrome** (Anthropic's browser extension) has been tested with DeepWork and is recommended for new users. Don't assume a default—confirm the tool before designing browser-dependent steps.
-
-### Step 3: Validate the Workflow
-
-After gathering information about all steps:
-
-1. **Review the flow**
- - Summarize the complete workflow
- - Show how outputs from one step feed into the next
- - Ask if anything is missing
-
-2. **Check for gaps**
- - Are there any steps where the input isn't clearly defined?
- - Are there any outputs that aren't used by later steps?
- - Are there circular dependencies?
-
-3. **Confirm details**
- - Job name (lowercase, underscores, descriptive)
- - Job summary (one clear sentence, max 200 chars)
- - Job description (detailed multi-line explanation)
- - Version number (start with 1.0.0)
-
-### Step 4: Define Quality Validation (Stop Hooks)
-
-For each step, consider whether it would benefit from **quality validation loops**. Stop hooks allow the AI agent to iteratively refine its work until quality criteria are met.
-
-**Ask structured questions about quality validation:**
-- "Are there specific quality criteria that must be met for this step?"
-- "Would you like the agent to validate its work before completing?"
-- "What would make you send the work back for revision?"
-
-**Stop hooks are particularly valuable for:**
-- Steps with complex outputs that need multiple checks
-- Steps where quality is critical (final deliverables)
-- Steps with subjective quality criteria that benefit from AI self-review
-
-**Three types of stop hooks are supported:**
-
-1. **Inline Prompt** (`prompt`) - Best for simple quality criteria
- ```yaml
- stop_hooks:
- - prompt: |
- Verify the output meets these criteria:
- 1. Contains at least 5 competitors
- 2. Each competitor has a description
- 3. Selection rationale is clear
- ```
-
-2. **Prompt File** (`prompt_file`) - For detailed/reusable criteria
- ```yaml
- stop_hooks:
- - prompt_file: hooks/quality_check.md
- ```
-
-3. **Script** (`script`) - For programmatic validation (tests, linting)
- ```yaml
- stop_hooks:
- - script: hooks/run_tests.sh
- ```
-
-**Multiple hooks can be combined:**
-```yaml
-stop_hooks:
- - script: hooks/lint_output.sh
- - prompt: "Verify the content is comprehensive and well-organized"
-```
-
-**Encourage prompt-based hooks** - They leverage the AI's ability to understand context and make nuanced quality judgments. Script hooks are best for objective checks (syntax, format, tests).
-
-### Step 5: Create the Job Directory and Specification
-
-Only after you have complete understanding, create the job directory and `job.yml` file:
-
-**First, create the directory structure** using the `make_new_job.sh` script:
-
-```bash
-.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
-```
-
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - For step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - For custom validation scripts
-- `.deepwork/jobs/[job_name]/templates/` - For example file formats
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Then create the job.yml file** at `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the NEW job you're creating, e.g., `competitive_research`)
-
-**Doc Spec**: See `.deepwork/doc_specs/job_spec.md` for the complete specification with quality criteria.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/job.yml.example` for a fully worked example.
-
-**Important**:
-- Use lowercase with underscores for job name and step IDs
-- Ensure file inputs reference steps in dependencies
-- No circular dependencies
-- At least one output per step
-- The `summary` should be concise (max 200 chars)
-- The `description` should provide rich context for future refinement
-- Include a `changelog` section with an initial entry for version 1.0.0
-
-## Example Dialog
-
-```
-Claude: I'll help you define a new DeepWork job. Let's start by understanding what you're trying to accomplish.
-
-What complex task or workflow are you trying to create? What's the overall goal?
-
-User: I want to create competitive research reports for my company
-
-Claude: Great! Let's dig into that. A few questions:
-
-1. What's the final deliverable? Is it a written report, a presentation, or something else?
-2. Who is the audience? Your team, executives, clients?
-3. How comprehensive should these reports be?
-
-User: It's a written report for our product team. Should be comprehensive - analyzing 3-5 competitors.
-
-Claude: Perfect. Now let's think about the major phases of creating this report. What are the main stages you go through? For example:
-- Identifying which competitors to analyze?
-- Gathering information about them?
-- Analyzing the data?
-- Writing the report?
-
-Or is your process different?
-
-User: Yes, that's close. I would say:
-1. Identify competitors
-2. Research each one
-3. Create comparison analysis
-4. Write positioning recommendations
-
-Claude: Excellent! Now let's define each step in detail. Starting with "Identify competitors":
-
-1. What inputs do you need to start? Do you already have a market segment in mind, or do you need to specify that?
-2. What should the output look like? A list of competitor names? More detailed?
-3. How many competitors should be identified?
-
-User: I need to specify the market segment and product category as inputs. The output should be a markdown file with 5-7 competitors and brief descriptions of each.
-
-Claude: Perfect. A few more questions about that output:
-1. What should the file be called? Something like `competitors_list.md`?
-2. Should it go in the project root, or in a subdirectory like `research/` or `data/`?
-
-User: Just call it `competitors_list.md` in the project root.
-
-Claude: Great. Now for the "Research each one" step:
-1. Does it use the competitor list from the first step as input?
-2. What kind of research - product features, pricing, marketing, all of the above?
-3. What format should the research output be in?
-4. Where should that research be saved?
-
-[continues for each step...]
-
-[After gathering all information:]
-
-Claude: Let me summarize the workflow I've designed based on our conversation:
-
-**Job: competitive_research**
-Summary: Systematic competitive analysis workflow for product positioning
-
-Description:
-A comprehensive workflow for analyzing competitors in your market segment. This job helps product teams understand the competitive landscape by systematically identifying competitors, researching their offerings, creating comparison matrices, and developing strategic positioning recommendations.
-
-The workflow produces:
-- A vetted list of key competitors
-- Detailed research notes on each competitor
-- A comparison matrix highlighting key differentiators
-- Strategic positioning recommendations
-
-Designed for product teams conducting quarterly competitive analysis.
-
-**Steps:**
-
-Step 1: identify_competitors
-- Input: market_segment (user), product_category (user)
-- Output: competitors_list.md (5-7 competitors with descriptions)
-
-Step 2: research_competitors
-- Input: competitors_list.md (from step 1)
-- Output: research_notes.md (detailed findings for each competitor)
-- Depends on: identify_competitors
-
-Step 3: comparative_analysis
-- Input: research_notes.md (from step 2)
-- Output: comparison_matrix.md (side-by-side analysis)
-- Depends on: research_competitors
-
-Step 4: positioning_recommendations
-- Input: comparison_matrix.md (from step 3)
-- Output: positioning_report.md (strategic recommendations)
-- Depends on: comparative_analysis
-
-Does this accurately capture your workflow? Any changes needed?
-
-User: Yes, that's perfect!
-
-Claude: Great! Creating the job.yml specification now...
-
-[Creates .deepwork/jobs/competitive_research/job.yml with the complete spec]
-
-✓ Job specification created!
-
-**File created:**
-- .deepwork/jobs/competitive_research/job.yml
-
-**Next step:**
-Run `/deepwork_jobs.review_job_spec` to validate the specification against quality criteria.
-```
-
-## Important Guidelines
-
-1. **Focus on specification only** - Don't create instruction files yet
-2. **Ask structured questions** - Never skip the discovery phase; use the AskUserQuestion tool
-3. **Rich context in description** - This helps with future refinement
-4. **Validate understanding** - Summarize and confirm before creating
-5. **Use examples** - Help users understand what good specifications look like
-6. **Understand file organization** - Always ask structured questions about where outputs should be saved and if subdirectories are needed
-
-## Validation Rules
-
-Before creating the job.yml, ensure:
-- Job name: lowercase, underscores, no spaces
-- Version: semantic versioning (1.0.0)
-- Summary: concise, under 200 characters
-- Description: detailed, provides context
-- Step IDs: unique, descriptive, lowercase with underscores
-- Dependencies: must reference existing step IDs
-- File inputs: `from_step` must be in dependencies
-- At least one output per step
-- Outputs can be filenames (e.g., `report.md`) or paths (e.g., `reports/analysis.md`)
-- File paths in outputs should match where files will actually be created
-- No circular dependencies
-
-## Output Format
-
-### job.yml
-
-The complete YAML specification file (example shown in Step 5 above).
-
-**Location**: `.deepwork/jobs/[job_name]/job.yml`
-
-(Where `[job_name]` is the name of the new job being created)
-
-After creating the file:
-1. Inform the user that the specification is complete
-2. Recommend that they review the job.yml file
-3. Tell them to run `/deepwork_jobs.review_job_spec` next
-
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md
deleted file mode 100644
index 749c8c6f..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md
+++ /dev/null
@@ -1,233 +0,0 @@
-# Implement Job Steps
-
-## Objective
-
-Generate the DeepWork job directory structure and instruction files for each step based on the validated `job.yml` specification from the review_job_spec step.
-
-## Task
-
-Read the `job.yml` specification file and create all the necessary files to make the job functional, including directory structure and step instruction files. Then sync the commands to make them available.
-
-### Step 1: Create Directory Structure Using Script
-
-Run the `make_new_job.sh` script to create the standard directory structure:
-
-```bash
-.deepwork/jobs/deepwork_jobs/make_new_job.sh [job_name]
-```
-
-This creates:
-- `.deepwork/jobs/[job_name]/` - Main job directory
-- `.deepwork/jobs/[job_name]/steps/` - Step instruction files
-- `.deepwork/jobs/[job_name]/hooks/` - Custom validation scripts (with .gitkeep)
-- `.deepwork/jobs/[job_name]/templates/` - Example file formats (with .gitkeep)
-- `.deepwork/jobs/[job_name]/AGENTS.md` - Job management guidance
-
-**Note**: If the directory already exists (e.g., job.yml was created by define step), you can skip this step or manually create the additional directories:
-```bash
-mkdir -p .deepwork/jobs/[job_name]/hooks .deepwork/jobs/[job_name]/templates
-touch .deepwork/jobs/[job_name]/hooks/.gitkeep .deepwork/jobs/[job_name]/templates/.gitkeep
-```
-
-### Step 2: Read and Validate the Specification
-
-1. **Locate the job.yml file**
- - Read `.deepwork/jobs/[job_name]/job.yml` from the review_job_spec step
- - Parse the YAML content
-
-2. **Validate the specification**
- - Ensure it follows the schema (name, version, summary, description, steps)
- - Check that all dependencies reference existing steps
- - Verify no circular dependencies
- - Confirm file inputs match dependencies
-
-3. **Extract key information**
- - Job name, version, summary, description
- - List of all steps with their details
- - Understand the workflow structure
-
-### Step 3: Generate Step Instruction Files
-
-For each step in the job.yml, create a comprehensive instruction file at `.deepwork/jobs/[job_name]/steps/[step_id].md`.
-
-**Template reference**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.template` for the standard structure.
-
-**Complete example**: See `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example` for a fully worked example.
-
-**Available templates in `.deepwork/jobs/deepwork_jobs/templates/`:**
-- `job.yml.template` - Job specification structure
-- `step_instruction.md.template` - Step instruction file structure
-- `agents.md.template` - AGENTS.md file structure
-- `job.yml.example` - Complete job specification example
-- `step_instruction.md.example` - Complete step instruction example
-
-**Guidelines for generating instructions:**
-
-1. **Use the job description** - The detailed description from job.yml provides crucial context
-2. **Be specific** - Don't write generic instructions; tailor them to the step's purpose
-3. **Provide examples** - Show what good output looks like
-4. **Explain the "why"** - Help the user understand the step's role in the workflow
-5. **Quality over quantity** - Detailed, actionable instructions are better than vague ones
-6. **Align with stop hooks** - If the step has `stop_hooks` defined, ensure the quality criteria in the instruction file match the validation criteria in the hooks
-7. **Ask structured questions** - When a step has user inputs, the instructions MUST explicitly tell the agent to "ask structured questions" using the AskUserQuestion tool to gather that information. Never use generic phrasing like "ask the user" - always use "ask structured questions"
-
-### Handling Stop Hooks
-
-If a step in the job.yml has `stop_hooks` defined, the generated instruction file should:
-
-1. **Mirror the quality criteria** - The "Quality Criteria" section should match what the stop hooks will validate
-2. **Be explicit about success** - Help the agent understand when the step is truly complete
-3. **Include the promise pattern** - Mention that `✓ Quality Criteria Met` should be included when criteria are met
-
-**Example: If the job.yml has:**
-```yaml
-- id: research_competitors
- name: "Research Competitors"
- stop_hooks:
- - prompt: |
- Verify the research meets criteria:
- 1. Each competitor has at least 3 data points
- 2. Sources are cited
- 3. Information is current (within last year)
-```
-
-**The instruction file should include:**
-```markdown
-## Quality Criteria
-
-- Each competitor has at least 3 distinct data points
-- All information is sourced with citations
-- Data is current (from within the last year)
-- When all criteria are met, include `✓ Quality Criteria Met` in your response
-```
-
-This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing.
-
-### Using Supplementary Reference Files
-
-Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root.
-
-See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples.
-
-### Step 4: Verify job.yml Location
-
-Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define and review_job_spec steps should have created and validated it. If for some reason it's not there, you may need to create or move it.
-
-### Step 5: Sync Skills
-
-Run `deepwork sync` to generate the skills for this job:
-
-```bash
-deepwork sync
-```
-
-This will:
-- Parse the job definition
-- Generate skills for each step
-- Make the skills available in `.claude/skills/` (or appropriate platform directory)
-
-### Step 6: Consider Rules for the New Job
-
-After implementing the job, consider whether there are **rules** that would help enforce quality or consistency when working with this job's domain.
-
-**What are rules?**
-
-Rules are automated guardrails stored as markdown files in `.deepwork/rules/` that trigger when certain files change during an AI session. They help ensure:
-- Documentation stays in sync with code
-- Team guidelines are followed
-- Architectural decisions are respected
-- Quality standards are maintained
-
-**When to suggest rules:**
-
-Think about the job you just implemented and ask:
-- Does this job produce outputs that other files depend on?
-- Are there documentation files that should be updated when this job's outputs change?
-- Are there quality checks or reviews that should happen when certain files in this domain change?
-- Could changes to the job's output files impact other parts of the project?
-
-**Examples of rules that might make sense:**
-
-| Job Type | Potential Rule |
-|----------|----------------|
-| API Design | "Update API docs when endpoint definitions change" |
-| Database Schema | "Review migrations when schema files change" |
-| Competitive Research | "Update strategy docs when competitor analysis changes" |
-| Feature Development | "Update changelog when feature files change" |
-| Configuration Management | "Update install guide when config files change" |
-
-**How to offer rule creation:**
-
-If you identify one or more rules that would benefit the user, explain:
-1. **What the rule would do** - What triggers it and what action it prompts
-2. **Why it would help** - How it prevents common mistakes or keeps things in sync
-3. **What files it would watch** - The trigger patterns
-
-Then ask the user:
-
-> "Would you like me to create this rule for you? I can run `/deepwork_rules.define` to set it up."
-
-If the user agrees, invoke the `/deepwork_rules.define` command to guide them through creating the rule.
-
-**Example dialogue:**
-
-```
-Based on the competitive_research job you just created, I noticed that when
-competitor analysis files change, it would be helpful to remind you to update
-your strategy documentation.
-
-I'd suggest a rule like:
-- **Name**: "Update strategy when competitor analysis changes"
-- **Trigger**: `**/positioning_report.md`
-- **Action**: Prompt to review and update `docs/strategy.md`
-
-Would you like me to create this rule? I can run `/deepwork_rules.define` to set it up.
-```
-
-**Note:** Not every job needs rules. Only suggest them when they would genuinely help maintain consistency or quality. Don't force rules where they don't make sense.
-
-## Example Implementation
-
-For a complete worked example showing a job.yml and corresponding step instruction file, see:
-- **Job specification**: `.deepwork/jobs/deepwork_jobs/templates/job.yml.example`
-- **Step instruction**: `.deepwork/jobs/deepwork_jobs/templates/step_instruction.md.example`
-
-## Important Guidelines
-
-1. **Read the spec carefully** - Understand the job's intent from the description
-2. **Generate complete instructions** - Don't create placeholder or stub files
-3. **Maintain consistency** - Use the same structure for all step instruction files
-4. **Provide examples** - Show what good output looks like
-5. **Use context** - The job description provides valuable context for each step
-6. **Be specific** - Tailor instructions to the specific step, not generic advice
-
-## Validation Before Sync
-
-Before running `deepwork sync`, verify:
-- All directories exist
-- `job.yml` is in place
-- All step instruction files exist (one per step)
-- No file system errors
-
-## Completion Checklist
-
-Before marking this step complete, ensure:
-- [ ] job.yml validated and copied to job directory
-- [ ] All step instruction files created
-- [ ] Each instruction file is complete and actionable
-- [ ] `deepwork sync` executed successfully
-- [ ] Skills generated in platform directory
-- [ ] Considered whether rules would benefit this job (Step 6)
-- [ ] If rules suggested, offered to run `/deepwork_rules.define`
-
-## Quality Criteria
-
-- Job directory structure is correct
-- All instruction files are complete (not stubs)
-- Instructions are specific and actionable
-- Output examples are provided in each instruction file
-- Quality criteria defined for each step
-- Steps with user inputs explicitly use "ask structured questions" phrasing
-- Sync completed successfully
-- Skills available for use
-- Thoughtfully considered relevant rules for the job domain
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/learn.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/learn.md
deleted file mode 100644
index bfb393a5..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/steps/learn.md
+++ /dev/null
@@ -1,355 +0,0 @@
-# Learn from Job Execution
-
-## Objective
-
-Think deeply about this task. Reflect on the current conversation to identify learnings from DeepWork job executions, improve job instructions with generalizable insights, and capture bespoke (run-specific) learnings in AGENTS.md files in the deepest common folder that would contain all work on the topic in the future.
-
-## Task
-
-Analyze the conversation history to extract learnings and improvements, then apply them appropriately:
-- **Generalizable learnings** → Update job instruction files
-- **Bespoke learnings** (specific to this run) → Add to AGENTS.md in the deepest common folder for the topic
-
-### Step 1: Analyze Conversation for Job Executions
-
-1. **Scan the conversation** for DeepWork slash commands that were run
- - Look for patterns like `/job_name.step_id`
- - Identify which jobs and steps were executed
- - Note the order of execution
-
-2. **Identify the target folder**
- - This should be the deepest common folder that would contain all work on the topic in the future
- - Should be clear from conversation history where work was done
- - If unclear, run `git diff` to see where changes were made on the branch
-
-3. **If no job was specified**, ask the user:
- - "Which DeepWork job would you like me to learn from?"
- - List available jobs from `.deepwork/jobs/`
-
-### Step 2: Identify Points of Confusion and Inefficiency
-
-Review the conversation for:
-
-1. **Confusion signals**
- - Questions the agent asked that shouldn't have been necessary
- - Misunderstandings about what a step required
- - Incorrect outputs that needed correction
- - Ambiguous instructions that led to wrong interpretations
-
-2. **Inefficiency signals**
- - Extra steps or iterations that were needed
- - Information that had to be repeated
- - Context that was missing from instructions
- - Dependencies that weren't clear
-
-3. **Error patterns**
- - Failed validations and why they failed
- - Quality criteria that were misunderstood
- - Edge cases that weren't handled
-
-4. **Success patterns**
- - What worked particularly well
- - Efficient approaches worth preserving
- - Good examples that could be added to instructions
-
-### Step 3: Classify Learnings
-
-For each learning identified, determine if it is:
-
-**Generalizable** (should improve instructions):
-- Would help ANY future run of this job
-- Addresses unclear or missing guidance
-- Fixes incorrect assumptions in instructions
-- Adds helpful examples or context
-- Examples:
- - "Step instructions should mention that X format is required"
- - "Quality criteria should include checking for Y"
- - "Add example of correct output format"
-
-**doc spec-Related** (should improve doc spec files):
-- Improvements to document quality criteria
-- Changes to document structure or format
-- Updated audience or frequency information
-- Examples:
- - "The report should include a summary table"
- - "Quality criterion 'Visualization' needs clearer requirements"
- - "Documents need a section for action items"
-
-**Bespoke** (should go in AGENTS.md):
-- Specific to THIS project/codebase/run
-- Depends on local conventions or structure
-- References specific files or paths
-- Would not apply to other uses of this job
-- Examples:
- - "In this codebase, API endpoints are in `src/api/`"
- - "This project uses camelCase for function names"
- - "The main config file is at `config/settings.yml`"
-
-### Step 3.5: Identify doc spec-Related Learnings
-
-Review the conversation for doc spec-related improvements:
-
-1. **Quality Criteria Changes**
- - Were any quality criteria unclear or insufficient?
- - Did the agent repeatedly fail certain criteria?
- - Are there new criteria that should be added?
-
-2. **Document Structure Changes**
- - Did the user request different sections?
- - Were parts of the document format confusing?
- - Should the example document be updated?
-
-3. **Metadata Updates**
- - Has the target audience changed?
- - Should frequency or path patterns be updated?
-
-**Signals for doc spec improvements:**
-- User asked for changes to document format
-- Repeated validation failures on specific criteria
-- Feedback about missing sections or information
-- Changes to how documents are organized/stored
-
-### Step 4: Update Job Instructions (Generalizable Learnings)
-
-For each generalizable learning:
-
-1. **Locate the instruction file**
- - Path: `.deepwork/jobs/[job_name]/steps/[step_id].md`
-
-2. **Make targeted improvements**
- - Add missing context or clarification
- - Include helpful examples
- - Clarify ambiguous instructions
- - Update quality criteria if needed
-
-3. **Keep instructions concise**
- - Avoid redundancy - don't repeat the same guidance in multiple places
- - Be direct - remove verbose explanations that don't add value
- - Prefer bullet points over paragraphs where appropriate
-
-4. **Preserve instruction structure**
- - Keep existing sections (Objective, Task, Process, Output Format, Quality Criteria)
- - Add to appropriate sections rather than restructuring
- - Maintain consistency with other steps
-
-5. **Track changes for changelog**
- - Note what was changed and why
- - Prepare changelog entry for job.yml
-
-### Step 4b: Extract Shared Content into Referenced Files
-
-Review all instruction files for the job and identify content that:
-- Appears in multiple step instructions (duplicated)
-- Is lengthy and could be extracted for clarity
-- Would benefit from being maintained in one place
-
-**Extract to shared files:**
-
-1. **Create shared files** in `.deepwork/jobs/[job_name]/steps/shared/`
- - `conventions.md` - Coding/formatting conventions used across steps
- - `examples.md` - Common examples referenced by multiple steps
- - `schemas.md` - Data structures or formats used throughout
-
-2. **Reference from instructions** using markdown includes or explicit references:
- ```markdown
- ## Conventions
-
- Follow the conventions defined in `shared/conventions.md`.
- ```
-
-3. **Benefits of extraction:**
- - Single source of truth - update once, applies everywhere
- - Shorter instruction files - easier to read and maintain
- - Consistent guidance across steps
-
-### Step 4.5: Update doc spec Files (doc spec-Related Learnings)
-
-If doc spec-related learnings were identified:
-
-1. **Locate the doc spec file**
- - Find doc spec references in job.yml outputs (look for `doc_spec: .deepwork/doc_specs/[doc_spec_name].md`)
- - doc spec files are at `.deepwork/doc_specs/[doc_spec_name].md`
-
-2. **Update quality_criteria array**
- - Add new criteria with name and description
- - Modify existing criteria descriptions for clarity
- - Remove criteria that are no longer relevant
-
-3. **Update example document**
- - Modify the markdown body to reflect structure changes
- - Ensure the example matches updated criteria
-
-4. **Update metadata as needed**
- - target_audience: If audience has changed
- - frequency: If production cadence has changed
- - path_patterns: If storage location has changed
-
-**Example doc spec update:**
-```yaml
-# Before
-quality_criteria:
- - name: Visualization
- description: Include charts
-
-# After
-quality_criteria:
- - name: Visualization
- description: Include Mermaid.js charts showing spend breakdown by service and month-over-month trend
-```
-
-### Step 5: Create/Update AGENTS.md (Bespoke Learnings)
-
-The AGENTS.md file captures project-specific knowledge that helps future agent runs.
-
-1. **Determine the correct location**
- - Place AGENTS.md in the deepest common folder that would contain all work on the topic in the future
- - This ensures the knowledge is available when working in that context
- - If uncertain, place at the project root
-
-2. **Use file references where possible**
- - Instead of duplicating information, reference source files
- - This keeps AGENTS.md in sync as the codebase evolves
- - Pattern: "See `path/to/file.ext` for [description]"
-
-3. **AGENTS.md structure**: See `.deepwork/jobs/deepwork_jobs/templates/agents.md.template` for the standard format.
-
-4. **Writing entries**
- - Be concise but specific
- - Always prefer file references over inline content
- - Use line numbers when referencing specific code: `file.ext:42`
- - Group related learnings together
-
-### Step 6: Update Job Version and Changelog
-
-If instruction files were modified:
-
-1. **Bump version in job.yml**
- - Patch version (0.0.x) for instruction improvements
- - Minor version (0.x.0) if quality criteria changed
-
-2. **Add changelog entry**
- ```yaml
- - version: "[new_version]"
- changes: "Improved [step] instructions based on execution learnings: [brief description]"
- ```
-
-### Step 7: Sync Skills
-
-**Run deepwork sync** (if instructions were modified)
-```bash
-deepwork sync
-```
-
-## File Reference Patterns
-
-When adding entries to AGENTS.md, prefer these patterns:
-
-**Good (references)**:
-```markdown
-- API endpoints follow REST conventions. See `src/api/routes.ts` for examples.
-- Error handling pattern: See `src/utils/errors.ts:15-30`
-- Configuration schema: Defined in `config/schema.json`
-```
-
-**Avoid (duplicating)**:
-```markdown
-- API endpoints should return JSON with this format: { status: ..., data: ... }
-- Error handling should use try/catch with custom error classes
-- Configuration requires these fields: name, version, ...
-```
-
-## Quality Criteria
-
-- Conversation has been analyzed for job executions
-- Points of confusion and inefficiency are identified
-- Learnings are correctly classified (generalizable vs bespoke)
-- Job instructions updated for generalizable improvements
-- Instructions are concise - no redundancy or unnecessary verbosity
-- Shared/lengthy content extracted into referenced files where appropriate
-- AGENTS.md created/updated with bespoke learnings
-- File references used instead of duplicating content
-- AGENTS.md is in the correct folder (the deepest common folder for the topic)
-- When all criteria are met, include `✓ Quality Criteria Met`
-
-## Example Dialog
-
-```
-User: /deepwork_jobs.learn
-
-Claude: I'll analyze this conversation for learnings from DeepWork job executions.
-
-**Conversation Analysis**
-
-I found the following job executions:
-1. `/competitive_research.identify_competitors` - Completed
-2. `/competitive_research.research_competitors` - Completed with 2 iterations
-3. `/competitive_research.comparative_analysis` - Completed
-
-**Identified Issues**
-
-1. **Confusion in step 2**: The instructions didn't specify that competitor research should include pricing information. This caused an extra iteration.
-
-2. **Missing context**: The agent had to ask about the industry focus, which should have been carried forward from step 1.
-
-**Classification**
-
-- Generalizable: Add pricing to research requirements in instructions
-- Bespoke: This project focuses on B2B SaaS in the HR tech space
-
-**Actions Taken**
-
-1. Updated `.deepwork/jobs/competitive_research/steps/research_competitors.md`:
- - Added "Include pricing information (tiers, models, approximate ranges)" to research requirements
- - Added note about carrying forward industry context from previous step
-
-2. Created `./competitive_research_output/AGENTS.md`:
- ```markdown
- # Project Context for Competitive Research
-
- ## Job-Specific Context
-
- ### competitive_research
-
- #### Industry Focus
- - Domain: B2B SaaS, HR Technology sector
- - See `identify_competitors_output.md` for market definition
-
- #### Competitors Identified
- - Reference: `competitor_list.md` for full details
-
- ## Last Updated
- - Date: 2024-01-15
- - From conversation about: Initial competitive analysis run
- ```
-
-3. Updated job.yml version to 1.0.1 with changelog entry
-
-4. Ran `deepwork sync`
-
-**Summary**
-
-Updated job instructions and created AGENTS.md with bespoke learnings.
-```
-
-## Handling Edge Cases
-
-**No job executions found**:
-- Ask user which job they'd like to analyze
-- Or offer to review available jobs
-
-**Multiple jobs executed**:
-- Analyze each job separately
-- Create separate AGENTS.md entries or files as appropriate
-
-**AGENTS.md already exists**:
-- Read existing content
-- Append new learnings to appropriate sections
-- Update "Last Updated" section
-
-**No issues found**:
-- Document what worked well
-- Consider if any successful patterns should be added to instructions as examples
-
-**Sensitive information**:
-- Never include secrets, credentials, or PII in AGENTS.md
-- Reference config files instead of including values
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md
deleted file mode 100644
index fcc0ae9c..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# Review Job Specification
-
-## Objective
-
-Review the `job.yml` created in the define step against the doc spec quality criteria using a sub-agent for unbiased evaluation, then iterate on fixes until all criteria pass.
-
-## Why This Step Exists
-
-The define step focuses on understanding user requirements and creating a job specification. This review step ensures the specification meets quality standards before implementation. Using a sub-agent provides an unbiased "fresh eyes" review that catches issues the main agent might miss after being deeply involved in the definition process.
-
-## Task
-
-Use a sub-agent to review the job.yml against all 9 doc spec quality criteria, then fix any failed criteria. Repeat until all criteria pass.
-
-### Step 1: Read the Job Specification
-
-Read the `job.yml` file created in the define step:
-
-```
-.deepwork/jobs/[job_name]/job.yml
-```
-
-Also read the doc spec for reference:
-
-```
-.deepwork/doc_specs/job_spec.md
-```
-
-### Step 2: Spawn Review Sub-Agent
-
-Use the Task tool to spawn a sub-agent that will provide an unbiased review:
-
-```
-Task tool parameters:
-- subagent_type: "general-purpose"
-- model: "haiku"
-- description: "Review job.yml against doc spec"
-- prompt: [see below]
-```
-
-**Sub-agent prompt template:**
-
-```
-Review this job.yml against the following 9 quality criteria from the doc spec.
-
-For each criterion, respond with:
-- PASS or FAIL
-- If FAIL: specific issue and suggested fix
-
-## job.yml Content
-
-[paste the full job.yml content here]
-
-## Quality Criteria
-
-1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
-
-2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
-
-3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
-
-4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
-
-5. **Changelog Present**: Must include a changelog array with at least the initial version entry
-
-6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
-
-7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
-
-8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
-
-9. **Output Paths**: Outputs must be valid filenames or paths (e.g., `report.md` or `reports/analysis.md`)
-
-## Response Format
-
-Respond with a structured evaluation:
-
-### Overall: [X/9 PASS]
-
-### Criterion Results
-
-1. Valid Identifier: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-2. Semantic Version: [PASS/FAIL]
- [If FAIL: Issue and fix]
-
-[... continue for all 9 criteria ...]
-
-### Summary of Required Fixes
-
-[List any fixes needed, or "No fixes required - all criteria pass"]
-```
-
-### Step 3: Review Sub-Agent Findings
-
-Parse the sub-agent's response:
-
-1. **Count passing criteria** - How many of the 9 criteria passed?
-2. **Identify failures** - List specific criteria that failed
-3. **Note suggested fixes** - What changes does the sub-agent recommend?
-
-### Step 4: Fix Failed Criteria
-
-For each failed criterion, edit the job.yml to address the issue:
-
-**Common fixes by criterion:**
-
-| Criterion | Common Issue | Fix |
-|-----------|-------------|-----|
-| Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
-| Semantic Version | Missing or invalid format | Set to `"1.0.0"` or fix format |
-| Concise Summary | Too long or vague | Shorten to <200 chars, be specific |
-| Rich Description | Single line or missing context | Add multi-line explanation with problem/process/outcome/users |
-| Changelog Present | Missing changelog | Add `changelog:` with initial version entry |
-| Complete Steps | Missing required fields | Add id, name, description, instructions_file, outputs, dependencies |
-| Valid Dependencies | Non-existent step or circular | Fix step ID reference or reorder dependencies |
-| Input Consistency | from_step not in dependencies | Add the referenced step to dependencies array |
-| Output Paths | Invalid characters or format | Use valid filename/path format |
-
-### Step 5: Re-Run Review (If Needed)
-
-If any criteria failed:
-
-1. **Spawn a new sub-agent** with the updated job.yml content
-2. **Review the new findings**
-3. **Fix any remaining issues**
-4. **Repeat until all 9 criteria pass**
-
-### Step 6: Confirm Completion
-
-When all 9 criteria pass:
-
-1. **Announce success**: "All 9 doc spec quality criteria pass."
-2. **List what was validated**:
- - Valid Identifier
- - Semantic Version
- - Concise Summary
- - Rich Description
- - Changelog Present
- - Complete Steps
- - Valid Dependencies
- - Input Consistency
- - Output Paths
-3. **Include the promise**: `Quality Criteria Met`
-4. **Guide to next step**: "Run `/deepwork_jobs.implement` to generate the step instruction files."
-
-## Example Review Cycle
-
-```
-Claude: Let me review the job.yml against the doc spec quality criteria.
-
-[Spawns sub-agent with haiku model]
-
-Sub-agent response:
-### Overall: 7/9 PASS
-
-### Criterion Results
-1. Valid Identifier: PASS
-2. Semantic Version: PASS
-3. Concise Summary: FAIL
- Issue: Summary is 245 characters, exceeds 200 char limit
- Fix: Shorten to "Competitive analysis workflow for product positioning"
-4. Rich Description: PASS
-5. Changelog Present: FAIL
- Issue: No changelog array present
- Fix: Add changelog with initial version entry
-6. Complete Steps: PASS
-7. Valid Dependencies: PASS
-8. Input Consistency: PASS
-9. Output Paths: PASS
-
-### Summary of Required Fixes
-1. Shorten summary to under 200 characters
-2. Add changelog array with initial version
-
-Claude: I found 2 issues. Let me fix them.
-
-[Edits job.yml to shorten summary and add changelog]
-
-Claude: Let me verify the fixes with another review.
-
-[Spawns new sub-agent]
-
-Sub-agent response:
-### Overall: 9/9 PASS
-
-### Summary of Required Fixes
-No fixes required - all criteria pass
-
-Claude: All 9 doc spec quality criteria pass.
-
-Quality Criteria Met
-
-**Next step:** Run `/deepwork_jobs.implement` to generate the step instruction files.
-```
-
-## Quality Criteria
-
-- **Sub-Agent Used**: A sub-agent was spawned to provide unbiased review (not just self-review)
-- **All doc spec Criteria Evaluated**: The sub-agent assessed all 9 quality criteria from the doc spec
-- **Findings Addressed**: All failed criteria were fixed by the main agent
-- **Validation Loop Complete**: The review-fix cycle continued until all criteria passed
-- **Promise Included**: The response includes `Quality Criteria Met` when complete
-
-## Output
-
-The validated `job.yml` file at `.deepwork/jobs/[job_name]/job.yml` that passes all 9 doc spec quality criteria.
diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md
deleted file mode 100644
index 81b6494a..00000000
--- a/src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Supplementary Reference Files
-
-Step instructions can include additional `.md` files in the `steps/` directory. These supplementary files are useful for:
-
-- Providing detailed examples or templates that would clutter the main instruction file
-- Sharing common reference material across multiple steps
-- Including technical specifications, API documentation, or style guides
-
-## How to Use
-
-1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files
-2. Reference them in your step instructions using the **full path from the project root**
-
-## Example
-
-If you have a job called `my_job` and want to include an API specification template:
-
-1. Create the file at `.deepwork/jobs/my_job/steps/api_spec.md`
-2. Reference it in your step instructions like this:
-
-```markdown
-Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints.
-```
-
-## Path Format
-
-Always use the full relative path from the project root:
-
-```
-.deepwork/jobs/[job_name]/steps/[filename].md
-```
-
-For example:
-- `.deepwork/jobs/competitive_research/steps/competitor_template.md`
-- `.deepwork/jobs/api_design/steps/endpoint_schema.md`
-- `.deepwork/jobs/onboarding/steps/checklist_template.md`
-
-## Benefits
-
-Using supplementary files keeps your main step instructions focused and readable while allowing you to provide detailed reference material when needed. The AI agent can read these files during execution to get additional context.
diff --git a/src/deepwork/standard_jobs/deepwork_rules/job.yml b/src/deepwork/standard_jobs/deepwork_rules/job.yml
deleted file mode 100644
index a0032b9e..00000000
--- a/src/deepwork/standard_jobs/deepwork_rules/job.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: deepwork_rules
-version: "0.4.0"
-summary: "Creates file-change rules that enforce guidelines during AI sessions. Use when automating documentation sync or code review triggers."
-description: |
- Manages rules that automatically trigger when certain files change during an AI agent session.
- Rules help ensure that code changes follow team guidelines, documentation is updated,
- and architectural decisions are respected.
-
- IMPORTANT: Rules are evaluated at the "Stop" hook, which fires when an agent finishes its turn.
- This includes when sub-agents complete their work. Rules are NOT evaluated immediately after
- each file edit - they batch up and run once at the end of the agent's response cycle.
- - Command action rules: Execute their command (e.g., `uv sync`) when the agent stops
- - Prompt action rules: Display instructions to the agent, blocking until addressed
-
- Rules are stored as individual markdown files with YAML frontmatter in the `.deepwork/rules/`
- directory. Each rule file specifies:
- - Detection mode: trigger/safety, set (bidirectional), or pair (directional)
- - Patterns: Glob patterns for matching files, with optional variable capture
- - Action type: prompt (default) to show instructions, or command to run a shell command
- - Instructions: Markdown content describing what the agent should do
-
- Example use cases:
- - Update installation docs when configuration files change
- - Require security review when authentication code is modified
- - Ensure API documentation stays in sync with API code
- - Enforce source/test file pairing
- - Auto-run `uv sync` when pyproject.toml changes (command action)
-
-changelog:
- - version: "0.1.0"
- changes: "Initial version"
- - version: "0.2.0"
- changes: "Standardized on 'ask structured questions' phrasing for user input"
- - version: "0.3.0"
- changes: "Migrated to v2 format - individual markdown files in .deepwork/rules/"
- - version: "0.4.0"
- changes: "Improved skill descriptions with third-person voice and 'Use when...' triggers for better discoverability"
-
-steps:
- - id: define
- name: "Define Rule"
- description: "Creates a rule file that triggers when specified files change. Use when setting up documentation sync, code review requirements, or automated commands."
- instructions_file: steps/define.md
- inputs:
- - name: rule_purpose
- description: "What guideline or constraint should this rule enforce?"
- outputs:
- - .deepwork/rules/{rule-name}.md
- dependencies: []
diff --git a/src/deepwork/standard_jobs/deepwork_rules/rules/.gitkeep b/src/deepwork/standard_jobs/deepwork_rules/rules/.gitkeep
deleted file mode 100644
index 429162b4..00000000
--- a/src/deepwork/standard_jobs/deepwork_rules/rules/.gitkeep
+++ /dev/null
@@ -1,13 +0,0 @@
-# This directory contains example rule templates.
-# Copy and customize these files to create your own rules.
-#
-# Rule files use YAML frontmatter in markdown format:
-#
-# ---
-# name: Rule Name
-# trigger: "pattern/**/*"
-# safety: "optional/pattern"
-# ---
-# Instructions in markdown here.
-#
-# See doc/rules_syntax.md for full documentation.
diff --git a/src/deepwork/templates/claude/agent-expert.md.jinja b/src/deepwork/templates/claude/agent-expert.md.jinja
new file mode 100644
index 00000000..a96524a3
--- /dev/null
+++ b/src/deepwork/templates/claude/agent-expert.md.jinja
@@ -0,0 +1,51 @@
+{#
+Template: agent-expert.md.jinja
+Purpose: Generates an expert agent file for Claude Code
+
+Template Variables:
+ - expert_name: string - Expert identifier (e.g., "rails-activejob")
+ - discovery_description: string - Short description for expert discovery
+ - full_expertise: string - Complete expertise knowledge payload
+ - has_workflows: bool - True if expert has workflows
+ - workflows: list - Array of workflow info:
+ - name: string - Workflow identifier
+ - summary: string - Short workflow summary
+ - step_count: int - Number of steps in workflow
+ - first_step: string - First step ID
+#}
+---
+name: {{ expert_name }}
+description: "{{ discovery_description | replace('"', '\\"') | replace('\n', ' ') | truncate(200) }}"
+---
+
+{{ full_expertise }}
+
+---
+
+## Topics
+
+Detailed documentation on specific subjects within this domain.
+
+$(deepwork topics --expert "{{ expert_name }}")
+
+---
+
+## Learnings
+
+Hard-fought insights from real experiences.
+
+$(deepwork learnings --expert "{{ expert_name }}")
+
+{% if has_workflows %}
+---
+
+## Workflows
+
+Expert-owned multi-step workflows:
+
+{% for workflow in workflows %}
+- **{{ workflow.name }}**: {{ workflow.summary }}
+ - {{ workflow.step_count }} steps
+ - Start: `/{{ expert_name }}.{{ workflow.first_step }}`
+{% endfor %}
+{% endif %}
diff --git a/src/deepwork/templates/claude/settings.json b/src/deepwork/templates/claude/settings.json
index 97d5d1be..6bca7b9e 100644
--- a/src/deepwork/templates/claude/settings.json
+++ b/src/deepwork/templates/claude/settings.json
@@ -5,7 +5,6 @@
"Edit(./.deepwork/**)",
"Write(./.deepwork/**)",
"Bash(deepwork:*)",
- "Bash(./.deepwork/jobs/deepwork_jobs/make_new_job.sh:*)",
"WebSearch"
]
}
diff --git a/src/deepwork/templates/claude/skill-job-meta.md.jinja b/src/deepwork/templates/claude/skill-job-meta.md.jinja
deleted file mode 100644
index ea258a87..00000000
--- a/src/deepwork/templates/claude/skill-job-meta.md.jinja
+++ /dev/null
@@ -1,147 +0,0 @@
-{#
-Template: skill-job-meta.md.jinja
-Purpose: Generates the job overview skill file for Claude Code
-
-Template Variables:
- - job_name: string - Job identifier (e.g., "competitive_research")
- - job_summary: string - Short one-line summary of the job
- - job_description: string|null - Full description (optional)
- - total_steps: int - Number of steps in the job
- - has_workflows: bool - True if workflows are defined
- - workflows: list - Array of workflow objects:
- - name: string - Workflow identifier
- - summary: string - Short description of workflow
- - steps: list[string] - Ordered list of step IDs
- - first_step: string - First step ID to start workflow
- - standalone_steps: list - Steps not in any workflow (same structure as steps)
- - steps: list - Array of step objects:
- - id: string - Step identifier
- - name: string - Human-readable step name
- - description: string - What the step does
- - command_name: string - Slash command (e.g., "job_name.step_id")
- - dependencies: list[string]|null - Required prior steps
- - is_standalone: bool - True if not in any workflow
- - workflow_name: string|null - Name of workflow if in one
-#}
----
-name: {{ job_name }}
-description: "{{ job_summary }}"
----
-
-# {{ job_name }}
-
-{{ job_summary }}
-
-> **CRITICAL**: Always invoke steps using the Skill tool. Never copy/paste step instructions directly.
-
-{% if job_description %}
-{{ job_description }}
-{% endif %}
-
-{% if has_workflows %}
-## Workflows
-
-{% for workflow in workflows %}
-### {{ workflow.name }}
-
-{{ workflow.summary }}
-
-**Steps in order**:
-{% for entry in workflow.step_entries %}
-{% if entry.is_concurrent %}
-{{ loop.index }}. **Concurrent Steps** - Execute the following tasks in parallel:
-{% for task in entry.concurrent_steps %}
- - **Background Task {{ task.task_number }}**: {{ task.id }} - {{ task.description }}
-{% endfor %}
-{% else %}
-{% set step_id = entry.step_ids[0] %}
-{% set step = steps | selectattr("id", "equalto", step_id) | first %}
-{{ loop.index }}. **{{ step_id }}** - {{ step.description if step else "Unknown step" }}
-{% endif %}
-{% endfor %}
-
-**Start workflow**: `/{{ job_name }}.{{ workflow.first_step }}`
-
-{% endfor %}
-{% endif %}
-{% if standalone_steps %}
-## Standalone Skills
-
-These skills can be run independently at any time:
-
-{% for step in standalone_steps %}
-- **{{ step.id }}** - {{ step.description }}
- Command: `/{{ step.command_name }}`
-{% endfor %}
-
-{% endif %}
-{% if not has_workflows and not standalone_steps %}
-## Available Steps
-
-{% for step in steps %}
-{{ loop.index }}. **{{ step.id }}** - {{ step.description }}{% if step.dependencies %} (requires: {{ step.dependencies | join(', ') }}){% endif %}
-
-{% endfor %}
-{% endif %}
-
-## Execution Instructions
-
-### Step 1: Analyze Intent
-
-Parse any text following `/{{ job_name }}` to determine user intent:
-{% if has_workflows %}
-{% for workflow in workflows %}
-- "{{ workflow.name }}" or related terms → start {{ workflow.name }} workflow at `{{ job_name }}.{{ workflow.first_step }}`
-{% endfor %}
-{% endif %}
-{% for step in standalone_steps %}
-- "{{ step.id }}" or related terms → run standalone skill `{{ step.command_name }}`
-{% endfor %}
-{% if not has_workflows and not standalone_steps %}
-{% for step in steps %}
-- "{{ step.id }}" or related terms → start at `{{ step.command_name }}`
-{% endfor %}
-{% endif %}
-
-### Step 2: Invoke Starting Step
-
-Use the Skill tool to invoke the identified starting step:
-{% if has_workflows and workflows %}
-```
-Skill tool: {{ job_name }}.{{ workflows[0].first_step }}
-```
-{% else %}
-```
-Skill tool: {{ steps[0].command_name }}
-```
-{% endif %}
-
-### Step 3: Continue Workflow Automatically
-
-After each step completes:
-1. Check if there's a next step in the workflow sequence
-2. Invoke the next step using the Skill tool
-3. Repeat until workflow is complete or user intervenes
-
-**Note**: Standalone skills do not auto-continue to other steps.
-
-### Handling Ambiguous Intent
-
-If user intent is unclear, use AskUserQuestion to clarify:
-{% if has_workflows %}
-- Present available workflows and standalone skills as options
-{% else %}
-- Present available steps as numbered options
-{% endif %}
-- Let user select the starting point
-
-## Guardrails
-
-- Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
-- Do NOT skip steps in a workflow unless the user explicitly requests it
-- Do NOT proceed to the next step if the current step's outputs are incomplete
-- Do NOT make assumptions about user intent; ask for clarification when ambiguous
-
-## Context Files
-
-- Job definition: `.deepwork/jobs/{{ job_name }}/job.yml`
diff --git a/src/deepwork/templates/claude/skill-workflow-meta.md.jinja b/src/deepwork/templates/claude/skill-workflow-meta.md.jinja
new file mode 100644
index 00000000..1d51cc8d
--- /dev/null
+++ b/src/deepwork/templates/claude/skill-workflow-meta.md.jinja
@@ -0,0 +1,90 @@
+{#
+Template: skill-workflow-meta.md.jinja
+Purpose: Generates the workflow overview skill file for Claude Code
+
+Template Variables:
+ - expert_name: string - Expert identifier (e.g., "deepwork-jobs")
+ - workflow_name: string - Workflow identifier (e.g., "new_job")
+ - workflow_version: string - Workflow version (e.g., "1.0.0")
+ - workflow_summary: string - Short one-line summary of the workflow
+ - workflow_description: string|null - Full description (optional)
+ - total_steps: int - Number of steps in the workflow
+ - first_step: string - First step ID to start workflow
+ - execution_entries: list - Array of execution order entries:
+ - is_concurrent: bool - True if steps can run in parallel
+ - step_ids: list[string] - Step IDs in this entry
+ - concurrent_steps: list|null - Details for concurrent steps (if is_concurrent)
+ - steps: list - Array of step objects:
+ - id: string - Step identifier
+ - name: string - Human-readable step name
+ - description: string - What the step does
+ - command_name: string - Slash command (e.g., "expert-name.step_id")
+ - dependencies: list[string]|null - Required prior steps
+ - exposed: bool - True if user-invocable
+#}
+---
+name: {{ expert_name }}.{{ workflow_name }}
+description: "{{ workflow_summary }}"
+---
+
+# {{ expert_name }}.{{ workflow_name }}
+
+{{ workflow_summary }}
+
+> **CRITICAL**: Always invoke steps using the Skill tool. Never copy/paste step instructions directly.
+
+{% if workflow_description %}
+{{ workflow_description }}
+{% endif %}
+
+## Steps in Order
+
+{% for entry in execution_entries %}
+{% if entry.is_concurrent %}
+{{ loop.index }}. **Concurrent Steps** - Execute the following tasks in parallel:
+{% for task in entry.concurrent_steps %}
+ - **Background Task {{ task.task_number }}**: {{ task.id }} - {{ task.description }}
+{% endfor %}
+{% else %}
+{% set step_id = entry.step_ids[0] %}
+{% set step = steps | selectattr("id", "equalto", step_id) | first %}
+{{ loop.index }}. **{{ step_id }}** - {{ step.description if step else "Unknown step" }}
+{% endif %}
+{% endfor %}
+
+**Start workflow**: `/{{ expert_name }}.{{ first_step }}`
+
+## Execution Instructions
+
+### Step 1: Analyze Intent
+
+Parse any text following `/{{ expert_name }}.{{ workflow_name }}` to determine user intent:
+{% for step in steps %}
+- "{{ step.id }}" or related terms → run step `{{ step.command_name }}`
+{% endfor %}
+- No specific step mentioned → start at first step `{{ expert_name }}.{{ first_step }}`
+
+### Step 2: Invoke Starting Step
+
+Use the Skill tool to invoke the identified starting step:
+```
+Skill tool: {{ expert_name }}.{{ first_step }}
+```
+
+### Step 3: Continue Workflow Automatically
+
+After each step completes:
+1. Check if there's a next step in the workflow sequence
+2. Invoke the next step using the Skill tool
+3. Repeat until workflow is complete or user intervenes
+
+## Guardrails
+
+- Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
+- Do NOT skip steps in a workflow unless the user explicitly requests it
+- Do NOT proceed to the next step if the current step's outputs are incomplete
+- Do NOT make assumptions about user intent; ask for clarification when ambiguous
+
+## Context Files
+
+- Workflow definition: `.deepwork/experts/{{ expert_name | replace('-', '_') }}/workflows/{{ workflow_name }}/workflow.yml`
diff --git a/src/deepwork/templates/claude/skill-job-step.md.jinja b/src/deepwork/templates/claude/skill-workflow-step.md.jinja
similarity index 71%
rename from src/deepwork/templates/claude/skill-job-step.md.jinja
rename to src/deepwork/templates/claude/skill-workflow-step.md.jinja
index ffb8622c..312ec4bc 100644
--- a/src/deepwork/templates/claude/skill-job-step.md.jinja
+++ b/src/deepwork/templates/claude/skill-workflow-step.md.jinja
@@ -1,32 +1,29 @@
{#
-Template: skill-job-step.md.jinja
-Purpose: Generates individual step skill files for Claude Code
+Template: skill-workflow-step.md.jinja
+Purpose: Generates individual step skill files for workflow steps
Template Variables:
- Job Context:
- - job_name: string - Job identifier
- - job_summary: string - Short job summary
- - job_description: string|null - Full job description
+ Expert Context:
+ - expert_name: string - Expert identifier (e.g., "deepwork-jobs")
+
+ Workflow Context:
+ - workflow_name: string - Workflow identifier (e.g., "new_job")
+ - workflow_version: string - Workflow version (e.g., "1.0.0")
+ - workflow_summary: string - Short workflow summary
+ - workflow_description: string|null - Full workflow description
+ - workflow_step_number: int - Position in workflow (1-indexed)
+ - workflow_total_steps: int - Total steps in this workflow
Step Metadata:
- step_id: string - Step identifier
+ - step_name: string - Human-readable step name
- step_description: string - What this step does
- - step_number: int - Position in steps array (1-indexed, for backward compat)
- - total_steps: int - Total steps in job
- - is_standalone: bool - True if step can run independently (not in any workflow)
- - exposed: bool - True if user can invoke directly (default: true)
- dependencies: list[string]|null - Required prior step IDs
- next_step: string|null - Next step ID in workflow
+ - prev_step: string|null - Previous step ID in workflow
+ - exposed: bool - True if user can invoke directly (default: false)
- instructions_file: string - Path to step instructions file
- Workflow Context (only if step is in a workflow):
- - workflow_name: string - Name of the workflow this step belongs to
- - workflow_summary: string - Summary of the workflow
- - workflow_step_number: int - Position in workflow (1-indexed)
- - workflow_total_steps: int - Total steps in this workflow
- - workflow_next_step: string|null - Next step ID in workflow
- - workflow_prev_step: string|null - Previous step ID in workflow
-
Step Content:
- instructions_content: string - Full instructions markdown
- user_inputs: list|null - User parameters to gather:
@@ -35,21 +32,20 @@ Template Variables:
- file_inputs: list|null - Files from previous steps:
- file: string - File path
- from_step: string - Source step ID
- - outputs: list[string]|null - Output file paths
+ - outputs: list[object]|null - Output specifications:
+ - file: string - Output file path
+ - has_doc_spec: bool - Whether doc spec is attached
+ - doc_spec: object|null - Doc spec details if attached
Quality & Hooks:
- quality_criteria: list[string]|null - Criteria for completion
- - stop_hooks: list|null - Stop hook configurations:
- - type: "script"|"prompt"
- - path: string (for script)
- - content: string (for prompt)
- hooks: dict|null - All hooks by event name (Stop, PreToolUse, etc.)
Agent Delegation:
- agent: string|null - Agent type (e.g., "general-purpose"). When set, adds context: fork
#}
---
-name: {{ job_name }}.{{ step_id }}
+name: {{ expert_name }}.{{ step_id }}
description: "{{ step_description }}"
{% if not exposed %}
user-invocable: false
@@ -85,7 +81,7 @@ hooks:
- hooks:
{% for hook in script_hooks %}
- type: command
- command: ".deepwork/jobs/{{ job_name }}/{{ hook.path }}"
+ command: ".deepwork/experts/{{ expert_name | replace('-', '_') }}/workflows/{{ workflow_name }}/{{ hook.path }}"
{% endfor %}{#- for hook in script_hooks #}
{% endfor %}{#- for stop_event in ["Stop", "SubagentStop"] #}
{%- elif event_name != "SubagentStop" or "Stop" not in hooks %}
@@ -93,7 +89,7 @@ hooks:
- hooks:
{% for hook in script_hooks %}
- type: command
- command: ".deepwork/jobs/{{ job_name }}/{{ hook.path }}"
+ command: ".deepwork/experts/{{ expert_name | replace('-', '_') }}/workflows/{{ workflow_name }}/{{ hook.path }}"
{% endfor %}{#- for hook in script_hooks #}
{% endif %}{#- if event_name == "Stop" #}
{%- endif %}{#- if script_hooks #}
@@ -103,26 +99,18 @@ hooks:
---
-# {{ job_name }}.{{ step_id }}
+# {{ expert_name }}.{{ step_id }}
-{% if is_standalone %}
-**Standalone skill** - can be run anytime
-{% elif workflow_name %}
**Step {{ workflow_step_number }}/{{ workflow_total_steps }}** in **{{ workflow_name }}** workflow
> {{ workflow_summary }}
-{% else %}
-**Step {{ step_number }}/{{ total_steps }}** in **{{ job_name }}** workflow
-{% endif %}{#- if is_standalone #}
-
-> {{ job_summary }}
{% if dependencies %}
## Prerequisites (Verify First)
Before proceeding, confirm these steps are complete:
{% for dep in dependencies %}
-- `/{{ job_name }}.{{ dep }}`
+- `/{{ expert_name }}.{{ dep }}`
{% endfor %}{#- for dep in dependencies #}
{% endif %}{#- if dependencies #}
@@ -132,11 +120,11 @@ Before proceeding, confirm these steps are complete:
{{ instructions_content }}
-{% if job_description %}
-### Job Context
+{% if workflow_description %}
+### Workflow Context
-{{ job_description }}
-{% endif %}{#- if job_description #}
+{{ workflow_description }}
+{% endif %}{#- if workflow_description #}
{% if user_inputs or file_inputs %}
## Required Inputs
@@ -158,10 +146,10 @@ Before proceeding, confirm these steps are complete:
## Work Branch
-Use branch format: `deepwork/{{ job_name }}-[instance]-YYYYMMDD`
+Use branch format: `deepwork/{{ expert_name }}-{{ workflow_name }}-[instance]-YYYYMMDD`
- If on a matching work branch: continue using it
-- If on main/master: create new branch with `git checkout -b deepwork/{{ job_name }}-[instance]-$(date +%Y%m%d)`
+- If on main/master: create new branch with `git checkout -b deepwork/{{ expert_name }}-{{ workflow_name }}-[instance]-$(date +%Y%m%d)`
## Outputs
@@ -226,38 +214,25 @@ Use a sub-agent (Haiku model) to review your work against these criteria:
5. Only mark the step complete when the sub-agent confirms all criteria are satisfied
{% endif %}{#- if quality_criteria #}
-{% if stop_hooks -%}
-{% for hook in stop_hooks -%}
+{% if hooks -%}
+{% for event_name, event_hooks in hooks.items() -%}
+{% for hook in event_hooks -%}
{% if hook.type == "script" -%}
-**Validation script**: `.deepwork/jobs/{{ job_name }}/{{ hook.path }}` (runs automatically)
+**Validation script**: `.deepwork/experts/{{ expert_name | replace('-', '_') }}/workflows/{{ workflow_name }}/{{ hook.path }}` (runs automatically)
{% endif -%}{#- if hook.type == "script" #}
-{% endfor %}{#- for hook in stop_hooks #}
-{% endif %}{#- if stop_hooks #}
+{% endfor %}{#- for hook in event_hooks #}
+{% endfor %}{#- for event_name, event_hooks in hooks.items() #}
+{% endif %}{#- if hooks #}
## On Completion
-{% if is_standalone %}
-1. Verify outputs are created
-2. Inform user: "{{ step_id }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
-
-This standalone skill can be re-run anytime.
-{% elif workflow_name %}
1. Verify outputs are created
2. Inform user: "{{ workflow_name }} step {{ workflow_step_number }}/{{ workflow_total_steps }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
{% if next_step %}
-3. **Continue workflow**: Use Skill tool to invoke `/{{ job_name }}.{{ next_step }}`
+3. **Continue workflow**: Use Skill tool to invoke `/{{ expert_name }}.{{ next_step }}`
{% else %}
3. **{{ workflow_name }} workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
{% endif %}{#- if next_step #}
-{% else %}
-1. Verify outputs are created
-2. Inform user: "Step {{ step_number }}/{{ total_steps }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
-{% if next_step %}
-3. **Continue workflow**: Use Skill tool to invoke `/{{ job_name }}.{{ next_step }}`
-{% else %}
-3. **Workflow complete**: All steps finished. Consider creating a PR to merge the work branch.
-{% endif %}{#- if next_step #}
-{% endif %}{#- if is_standalone #}
---
-**Reference files**: `.deepwork/jobs/{{ job_name }}/job.yml`, `.deepwork/jobs/{{ job_name }}/{{ instructions_file }}`
+**Reference files**: `.deepwork/experts/{{ expert_name | replace('-', '_') }}/workflows/{{ workflow_name }}/workflow.yml`, `.deepwork/experts/{{ expert_name | replace('-', '_') }}/workflows/{{ workflow_name }}/{{ instructions_file }}`
diff --git a/src/deepwork/templates/gemini/skill-job-meta.toml.jinja b/src/deepwork/templates/gemini/skill-workflow-meta.toml.jinja
similarity index 72%
rename from src/deepwork/templates/gemini/skill-job-meta.toml.jinja
rename to src/deepwork/templates/gemini/skill-workflow-meta.toml.jinja
index 158790d7..8ba385c6 100644
--- a/src/deepwork/templates/gemini/skill-job-meta.toml.jinja
+++ b/src/deepwork/templates/gemini/skill-workflow-meta.toml.jinja
@@ -1,21 +1,23 @@
{#
-Template: skill-job-meta.toml.jinja
-Purpose: Generates the job overview skill file for Gemini CLI
+Template: skill-workflow-meta.toml.jinja
+Purpose: Generates the workflow overview skill file for Gemini CLI
Template Variables:
- - job_name: string - Job identifier (e.g., "competitive_research")
- - job_summary: string - Short one-line summary of the job
- - job_description: string|null - Full description (optional)
- - total_steps: int - Number of steps in the job
+ - expert_name: string - Expert identifier (e.g., "deepwork-jobs")
+ - workflow_name: string - Workflow identifier (e.g., "new_workflow")
+ - workflow_summary: string - Short one-line summary of the workflow
+ - workflow_description: string|null - Full description (optional)
+ - total_steps: int - Number of steps in the workflow
- steps: list - Array of step objects:
- id: string - Step identifier
- name: string - Human-readable step name
- description: string - What the step does
- - command_name: string - Slash command (e.g., "job_name:step_id")
+ - command_name: string - Slash command (e.g., "expert_name:step_id")
- dependencies: list[string]|null - Required prior steps
+ - exposed: bool - True if user-invocable
Note: Gemini uses TOML format with description + prompt fields.
- Commands use colon separator (/job_name:step_id) not period.
+ Commands use colon separator (/expert_name:step_id) not period.
#}
# {{ job_name }}
#
diff --git a/src/deepwork/templates/gemini/skill-job-step.toml.jinja b/src/deepwork/templates/gemini/skill-workflow-step.toml.jinja
similarity index 95%
rename from src/deepwork/templates/gemini/skill-job-step.toml.jinja
rename to src/deepwork/templates/gemini/skill-workflow-step.toml.jinja
index 946bec5c..a3352ef0 100644
--- a/src/deepwork/templates/gemini/skill-job-step.toml.jinja
+++ b/src/deepwork/templates/gemini/skill-workflow-step.toml.jinja
@@ -1,12 +1,12 @@
{#
-Template: skill-job-step.toml.jinja
+Template: skill-workflow-step.toml.jinja
Purpose: Generates individual step skill files for Gemini CLI
Template Variables:
- Job Context:
- - job_name: string - Job identifier
- - job_summary: string - Short job summary
- - job_description: string|null - Full job description
+ Expert/Workflow Context:
+ - expert_name: string - Expert identifier
+ - workflow_summary: string - Short workflow summary
+ - workflow_description: string|null - Full workflow description
Step Metadata:
- step_id: string - Step identifier
diff --git a/tests/e2e/test_claude_code_integration.py b/tests/e2e/test_claude_code_integration.py
deleted file mode 100644
index b98fbc28..00000000
--- a/tests/e2e/test_claude_code_integration.py
+++ /dev/null
@@ -1,274 +0,0 @@
-"""End-to-end tests for DeepWork with Claude Code integration.
-
-These tests validate that DeepWork-generated skills work correctly
-with Claude Code. The tests can run in two modes:
-
-1. **Generation-only mode** (default): Tests skill generation and structure
-2. **Full e2e mode**: Actually executes skills with Claude Code
-
-Set ANTHROPIC_API_KEY and DEEPWORK_E2E_FULL=true to run full e2e tests.
-"""
-
-import os
-import shutil
-import subprocess
-import tempfile
-from pathlib import Path
-
-import pytest
-
-from deepwork.core.adapters import ClaudeAdapter
-from deepwork.core.generator import SkillGenerator
-from deepwork.core.parser import parse_job_definition
-
-# Test input for deterministic validation
-TEST_INPUT = "apple, car, banana, chair, orange, table, mango, laptop, grape, bicycle"
-
-# Expected fruits from test input (for validation)
-EXPECTED_FRUITS = {"apple", "banana", "orange", "mango", "grape"}
-
-
-def has_claude_code() -> bool:
- """Check if Claude Code CLI is available."""
- try:
- result = subprocess.run(
- ["claude", "--version"],
- capture_output=True,
- timeout=10,
- )
- return result.returncode == 0
- except (FileNotFoundError, subprocess.TimeoutExpired):
- return False
-
-
-def has_api_key() -> bool:
- """Check if Anthropic API key is set."""
- return bool(os.environ.get("ANTHROPIC_API_KEY"))
-
-
-def run_full_e2e() -> bool:
- """Check if full e2e tests should run."""
- return (
- os.environ.get("DEEPWORK_E2E_FULL", "").lower() == "true"
- and has_api_key()
- and has_claude_code()
- )
-
-
-class TestSkillGenerationE2E:
- """End-to-end tests for skill generation."""
-
- def test_generate_fruits_skills_in_temp_project(self) -> None:
- """Test generating fruits skills in a realistic project structure."""
- with tempfile.TemporaryDirectory() as tmpdir:
- project_dir = Path(tmpdir)
-
- # Set up project structure
- deepwork_dir = project_dir / ".deepwork" / "jobs"
- deepwork_dir.mkdir(parents=True)
-
- # Copy fruits job fixture
- fixtures_dir = Path(__file__).parent.parent / "fixtures" / "jobs" / "fruits"
- shutil.copytree(fixtures_dir, deepwork_dir / "fruits")
-
- # Initialize git repo (required for some operations)
- subprocess.run(["git", "init"], cwd=project_dir, capture_output=True)
- subprocess.run(
- ["git", "config", "user.email", "test@test.com"],
- cwd=project_dir,
- capture_output=True,
- )
- subprocess.run(
- ["git", "config", "user.name", "Test"],
- cwd=project_dir,
- capture_output=True,
- )
-
- # Parse job and generate skills
- job = parse_job_definition(deepwork_dir / "fruits")
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skills_dir = project_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # Validate skills were generated (meta + steps)
- assert len(skill_paths) == 3 # 1 meta + 2 steps
-
- meta_skill = skills_dir / "skills" / "fruits" / "SKILL.md"
- identify_skill = skills_dir / "skills" / "fruits.identify" / "SKILL.md"
- classify_skill = skills_dir / "skills" / "fruits.classify" / "SKILL.md"
-
- assert meta_skill.exists()
- assert identify_skill.exists()
- assert classify_skill.exists()
-
- # Validate skill content
- identify_content = identify_skill.read_text()
- assert "# fruits.identify" in identify_content
- assert "raw_items" in identify_content
- assert "identified_fruits.md" in identify_content
-
- classify_content = classify_skill.read_text()
- assert "# fruits.classify" in classify_content
- assert "identified_fruits.md" in classify_content
- assert "classified_fruits.md" in classify_content
-
- def test_skill_structure_matches_claude_code_expectations(self) -> None:
- """Test that generated skills have the structure Claude Code expects."""
- fixtures_dir = Path(__file__).parent.parent / "fixtures" / "jobs" / "fruits"
- job = parse_job_definition(fixtures_dir)
-
- with tempfile.TemporaryDirectory() as tmpdir:
- skills_dir = Path(tmpdir) / ".claude"
- skills_dir.mkdir()
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- generator.generate_all_skills(job, adapter, skills_dir)
-
- # Step skills use directory/SKILL.md format
- identify_skill = skills_dir / "skills" / "fruits.identify" / "SKILL.md"
- content = identify_skill.read_text()
-
- # Claude Code expects specific sections
- assert "# fruits.identify" in content # Skill name header
- assert "## Instructions" in content # Instructions section
- assert "## Required Inputs" in content # Inputs section
- assert "## Outputs" in content # Outputs section
-
- # Check for user input prompt
- assert "raw_items" in content
-
- def test_dependency_chain_in_skills(self) -> None:
- """Test that dependency chain is correctly represented in skills."""
- fixtures_dir = Path(__file__).parent.parent / "fixtures" / "jobs" / "fruits"
- job = parse_job_definition(fixtures_dir)
-
- with tempfile.TemporaryDirectory() as tmpdir:
- skills_dir = Path(tmpdir) / ".claude"
- skills_dir.mkdir()
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- generator.generate_all_skills(job, adapter, skills_dir)
-
- # Step skills use directory/SKILL.md format
- # First step should have no prerequisites
- identify_skill = skills_dir / "skills" / "fruits.identify" / "SKILL.md"
- identify_content = identify_skill.read_text()
- assert "## Prerequisites" not in identify_content
-
- # Second step should reference first step
- classify_skill = skills_dir / "skills" / "fruits.classify" / "SKILL.md"
- classify_content = classify_skill.read_text()
- assert "## Prerequisites" in classify_content
- assert "identify" in classify_content.lower()
-
-
-@pytest.mark.skipif(
- not run_full_e2e(),
- reason="Full e2e requires ANTHROPIC_API_KEY, DEEPWORK_E2E_FULL=true, and claude CLI",
-)
-class TestClaudeCodeExecution:
- """End-to-end tests that actually execute with Claude Code.
-
- These tests only run when:
- - ANTHROPIC_API_KEY is set
- - DEEPWORK_E2E_FULL=true
- - Claude Code CLI is installed
- """
-
- @pytest.fixture
- def project_with_skills(self) -> Path:
- """Create a test project with generated skills."""
- tmpdir = tempfile.mkdtemp()
- project_dir = Path(tmpdir)
-
- # Set up project structure
- deepwork_dir = project_dir / ".deepwork" / "jobs"
- deepwork_dir.mkdir(parents=True)
-
- # Copy fruits job fixture
- fixtures_dir = Path(__file__).parent.parent / "fixtures" / "jobs" / "fruits"
- shutil.copytree(fixtures_dir, deepwork_dir / "fruits")
-
- # Initialize git repo
- subprocess.run(["git", "init"], cwd=project_dir, capture_output=True)
- subprocess.run(
- ["git", "config", "user.email", "test@test.com"],
- cwd=project_dir,
- capture_output=True,
- )
- subprocess.run(
- ["git", "config", "user.name", "Test"],
- cwd=project_dir,
- capture_output=True,
- )
-
- # Create README
- (project_dir / "README.md").write_text("# Test Project\n")
- subprocess.run(["git", "add", "."], cwd=project_dir, capture_output=True)
- subprocess.run(
- ["git", "commit", "-m", "init"],
- cwd=project_dir,
- capture_output=True,
- )
-
- # Generate skills
- job = parse_job_definition(deepwork_dir / "fruits")
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skills_dir = project_dir / ".claude"
- skills_dir.mkdir()
- generator.generate_all_skills(job, adapter, skills_dir)
-
- yield project_dir
-
- # Cleanup
- shutil.rmtree(tmpdir, ignore_errors=True)
-
- def test_fruits_workflow_execution(self, project_with_skills: Path) -> None:
- """Test executing the complete fruits workflow with Claude Code.
-
- Invokes /fruits once, which automatically runs all steps (identify + classify).
- """
- # Run Claude Code with the fruits skill - this executes the full workflow
- result = subprocess.run(
- ["claude", "--print", "/fruits"],
- input=f"raw_items: {TEST_INPUT}",
- cwd=project_with_skills,
- capture_output=True,
- text=True,
- timeout=300, # 5 minutes for full workflow
- )
-
- assert result.returncode == 0, f"Claude Code failed: {result.stderr}"
-
- # Verify identify step output was created
- identify_output = project_with_skills / "identified_fruits.md"
- assert identify_output.exists(), "identified_fruits.md was not created"
-
- # Validate identify output content
- identify_content = identify_output.read_text().lower()
- for fruit in EXPECTED_FRUITS:
- assert fruit in identify_content, (
- f"Expected fruit '{fruit}' not found in identified_fruits.md"
- )
-
- # Verify classify step output was created
- classify_output = project_with_skills / "classified_fruits.md"
- assert classify_output.exists(), "classified_fruits.md was not created"
-
- # Validate classify output has category structure
- classify_content = classify_output.read_text().lower()
- categories = ["citrus", "tropical", "pome", "berries", "grape"]
- has_category = any(cat in classify_content for cat in categories)
- assert has_category, f"No fruit categories found in output: {classify_content[:500]}"
-
- # Validate final output quality
- assert len(classify_content) > 100, "Output seems too short"
- assert "##" in classify_output.read_text(), "Output lacks markdown structure"
diff --git a/tests/integration/test_experts_sync.py b/tests/integration/test_experts_sync.py
new file mode 100644
index 00000000..010fe1be
--- /dev/null
+++ b/tests/integration/test_experts_sync.py
@@ -0,0 +1,260 @@
+"""Integration tests for expert sync functionality."""
+
+from pathlib import Path
+
+import pytest
+
+from deepwork.cli.sync import sync_skills
+from deepwork.utils.yaml_utils import save_yaml
+
+
+@pytest.fixture
+def project_with_experts(tmp_path: Path) -> Path:
+ """Create a minimal project with experts for sync testing."""
+ # Create .deepwork directory structure
+ deepwork_dir = tmp_path / ".deepwork"
+ deepwork_dir.mkdir()
+
+ # Create config.yml
+ config = {"version": "0.1.0", "platforms": ["claude"]}
+ save_yaml(deepwork_dir / "config.yml", config)
+
+ # Create experts directory with an expert
+ experts_dir = deepwork_dir / "experts"
+ experts_dir.mkdir()
+
+ expert_dir = experts_dir / "test_expert"
+ expert_dir.mkdir()
+
+ # Create expert.yml
+ (expert_dir / "expert.yml").write_text(
+ """discovery_description: |
+ Test expert for integration testing
+
+full_expertise: |
+ # Test Expert
+
+ You are an expert on testing integrations.
+
+ ## Key Concepts
+
+ - Integration testing
+ - End-to-end testing
+"""
+ )
+
+ # Create topics
+ topics_dir = expert_dir / "topics"
+ topics_dir.mkdir()
+ (topics_dir / "basics.md").write_text(
+ """---
+name: Testing Basics
+keywords:
+ - basics
+ - fundamentals
+last_updated: 2025-01-30
+---
+
+Content about testing basics.
+"""
+ )
+
+ # Create learnings
+ learnings_dir = expert_dir / "learnings"
+ learnings_dir.mkdir()
+ (learnings_dir / "discovery.md").write_text(
+ """---
+name: Important Discovery
+last_updated: 2025-01-20
+summarized_result: |
+ Found that integration tests should be isolated.
+---
+
+Details of the discovery.
+"""
+ )
+
+ # Create .claude directory
+ claude_dir = tmp_path / ".claude"
+ claude_dir.mkdir()
+
+ return tmp_path
+
+
+class TestExpertSync:
+ """Tests for expert sync functionality."""
+
+ def test_sync_creates_expert_agent(self, project_with_experts: Path) -> None:
+ """Test that sync creates expert agent files."""
+ # Run sync
+ sync_skills(project_with_experts)
+
+ # Check that agent was created
+ agent_file = project_with_experts / ".claude" / "agents" / "dwe_test-expert.md"
+ assert agent_file.exists(), f"Expected agent file at {agent_file}"
+
+ def test_sync_agent_has_correct_name(self, project_with_experts: Path) -> None:
+ """Test that generated agent has correct name field."""
+ sync_skills(project_with_experts)
+
+ agent_file = project_with_experts / ".claude" / "agents" / "dwe_test-expert.md"
+ content = agent_file.read_text()
+
+ assert "name: test-expert" in content
+
+ def test_sync_agent_has_description(self, project_with_experts: Path) -> None:
+ """Test that generated agent has description from discovery_description."""
+ sync_skills(project_with_experts)
+
+ agent_file = project_with_experts / ".claude" / "agents" / "dwe_test-expert.md"
+ content = agent_file.read_text()
+
+ assert "integration testing" in content.lower()
+
+ def test_sync_agent_has_full_expertise(self, project_with_experts: Path) -> None:
+ """Test that generated agent includes full_expertise content."""
+ sync_skills(project_with_experts)
+
+ agent_file = project_with_experts / ".claude" / "agents" / "dwe_test-expert.md"
+ content = agent_file.read_text()
+
+ assert "expert on testing integrations" in content
+ assert "Key Concepts" in content
+
+ def test_sync_agent_has_dynamic_topics_command(self, project_with_experts: Path) -> None:
+ """Test that generated agent includes dynamic topics command embedding."""
+ sync_skills(project_with_experts)
+
+ agent_file = project_with_experts / ".claude" / "agents" / "dwe_test-expert.md"
+ content = agent_file.read_text()
+
+ assert '$(deepwork topics --expert "test-expert")' in content
+
+ def test_sync_agent_has_dynamic_learnings_command(self, project_with_experts: Path) -> None:
+ """Test that generated agent includes dynamic learnings command embedding."""
+ sync_skills(project_with_experts)
+
+ agent_file = project_with_experts / ".claude" / "agents" / "dwe_test-expert.md"
+ content = agent_file.read_text()
+
+ assert '$(deepwork learnings --expert "test-expert")' in content
+
+
+class TestExpertSyncMultiple:
+ """Tests for syncing multiple experts."""
+
+ def test_sync_multiple_experts(self, tmp_path: Path) -> None:
+ """Test syncing multiple experts creates multiple agent files."""
+ # Create project structure
+ deepwork_dir = tmp_path / ".deepwork"
+ deepwork_dir.mkdir()
+ save_yaml(deepwork_dir / "config.yml", {"version": "0.1.0", "platforms": ["claude"]})
+
+ experts_dir = deepwork_dir / "experts"
+ experts_dir.mkdir()
+
+ # Create multiple experts
+ for name in ["expert_a", "expert_b", "expert_c"]:
+ expert_dir = experts_dir / name
+ expert_dir.mkdir()
+ (expert_dir / "expert.yml").write_text(
+ f"discovery_description: Expert {name}\nfull_expertise: Content for {name}."
+ )
+
+ (tmp_path / ".claude").mkdir()
+
+ # Run sync
+ sync_skills(tmp_path)
+
+ # Check all agents were created
+ agents_dir = tmp_path / ".claude" / "agents"
+ assert (agents_dir / "dwe_expert-a.md").exists()
+ assert (agents_dir / "dwe_expert-b.md").exists()
+ assert (agents_dir / "dwe_expert-c.md").exists()
+
+
+class TestExpertSyncNoExperts:
+ """Tests for syncing when no experts exist."""
+
+ def test_sync_no_experts_directory(self, tmp_path: Path) -> None:
+ """Test sync works when no experts directory exists."""
+ # Create minimal project without experts
+ deepwork_dir = tmp_path / ".deepwork"
+ deepwork_dir.mkdir()
+ save_yaml(deepwork_dir / "config.yml", {"version": "0.1.0", "platforms": ["claude"]})
+ (tmp_path / ".claude").mkdir()
+
+ # Should not raise
+ sync_skills(tmp_path)
+
+ # Agents directory may or may not exist, but should have no agent files
+ agents_dir = tmp_path / ".claude" / "agents"
+ if agents_dir.exists():
+ agent_files = list(agents_dir.glob("dwe_*.md"))
+ assert len(agent_files) == 0
+
+ def test_sync_empty_experts_directory(self, tmp_path: Path) -> None:
+ """Test sync works when experts directory is empty."""
+ # Create project with empty experts directory
+ deepwork_dir = tmp_path / ".deepwork"
+ deepwork_dir.mkdir()
+ save_yaml(deepwork_dir / "config.yml", {"version": "0.1.0", "platforms": ["claude"]})
+ (deepwork_dir / "experts").mkdir() # Empty experts dir
+ (tmp_path / ".claude").mkdir()
+
+ # Should not raise
+ sync_skills(tmp_path)
+
+
+class TestExpertSyncWithWorkflows:
+ """Tests for syncing experts with workflows."""
+
+ def test_sync_expert_with_workflow(self, tmp_path: Path) -> None:
+ """Test that sync handles experts with workflows."""
+ # Create project structure
+ deepwork_dir = tmp_path / ".deepwork"
+ deepwork_dir.mkdir()
+ save_yaml(deepwork_dir / "config.yml", {"version": "0.1.0", "platforms": ["claude"]})
+
+ # Create an expert with a workflow
+ expert_dir = deepwork_dir / "experts" / "test_expert"
+ expert_dir.mkdir(parents=True)
+ (expert_dir / "expert.yml").write_text(
+ "discovery_description: Test expert\nfull_expertise: Expert content."
+ )
+
+ # Create a workflow
+ workflow_dir = expert_dir / "workflows" / "test_workflow"
+ workflow_dir.mkdir(parents=True)
+ (workflow_dir / "workflow.yml").write_text(
+ """name: test_workflow
+version: "1.0.0"
+summary: A test workflow
+steps:
+ - id: step_one
+ name: Step One
+ description: First step
+ instructions_file: steps/step_one.md
+ outputs:
+ - output.md
+"""
+ )
+ steps_dir = workflow_dir / "steps"
+ steps_dir.mkdir()
+ (steps_dir / "step_one.md").write_text("Do the first step.")
+
+ (tmp_path / ".claude").mkdir()
+
+ # Run sync
+ sync_skills(tmp_path)
+
+ # Check workflow skills were created
+ skills_dir = tmp_path / ".claude" / "skills"
+ assert skills_dir.exists()
+ # Workflow should have created skills with format: expert.step_id
+ assert (skills_dir / "test-expert.step_one" / "SKILL.md").exists()
+
+ # Check expert agent was created
+ agents_dir = tmp_path / ".claude" / "agents"
+ assert agents_dir.exists()
+ assert (agents_dir / "dwe_test-expert.md").exists()
diff --git a/tests/integration/test_fruits_workflow.py b/tests/integration/test_fruits_workflow.py
deleted file mode 100644
index 8df8d956..00000000
--- a/tests/integration/test_fruits_workflow.py
+++ /dev/null
@@ -1,198 +0,0 @@
-"""Integration tests for the fruits CI test workflow.
-
-This module tests the fruits job - a simple, deterministic workflow
-designed for automated CI testing of the DeepWork framework.
-"""
-
-from pathlib import Path
-
-from deepwork.core.adapters import ClaudeAdapter
-from deepwork.core.generator import SkillGenerator
-from deepwork.core.parser import parse_job_definition
-
-
-class TestFruitsWorkflow:
- """Integration tests for the fruits CI test workflow."""
-
- def test_fruits_job_parses_correctly(self, fixtures_dir: Path) -> None:
- """Test that the fruits job definition parses correctly."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- assert job.name == "fruits"
- assert job.version == "1.0.0"
- assert len(job.steps) == 2
-
- # Verify step IDs
- step_ids = [step.id for step in job.steps]
- assert step_ids == ["identify", "classify"]
-
- def test_fruits_identify_step_structure(self, fixtures_dir: Path) -> None:
- """Test the identify step has correct structure."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- identify_step = job.steps[0]
- assert identify_step.id == "identify"
- assert identify_step.name == "Identify Fruits"
-
- # Has user input
- assert len(identify_step.inputs) == 1
- assert identify_step.inputs[0].is_user_input()
- assert identify_step.inputs[0].name == "raw_items"
-
- # Has output
- assert len(identify_step.outputs) == 1
- assert identify_step.outputs[0].file == "identified_fruits.md"
-
- # No dependencies (first step)
- assert identify_step.dependencies == []
-
- def test_fruits_classify_step_structure(self, fixtures_dir: Path) -> None:
- """Test the classify step has correct structure."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- classify_step = job.steps[1]
- assert classify_step.id == "classify"
- assert classify_step.name == "Classify Fruits"
-
- # Has file input from previous step
- assert len(classify_step.inputs) == 1
- assert classify_step.inputs[0].is_file_input()
- assert classify_step.inputs[0].file == "identified_fruits.md"
- assert classify_step.inputs[0].from_step == "identify"
-
- # Has output
- assert len(classify_step.outputs) == 1
- assert classify_step.outputs[0].file == "classified_fruits.md"
-
- # Depends on identify step
- assert classify_step.dependencies == ["identify"]
-
- def test_fruits_skill_generation(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test that fruits job generates valid Claude skills."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # Now includes meta-skill + step skills
- assert len(skill_paths) == 3 # 1 meta + 2 steps
-
- # Verify skill directories with SKILL.md files exist
- meta_skill = skills_dir / "skills" / "fruits" / "SKILL.md"
- identify_skill = skills_dir / "skills" / "fruits.identify" / "SKILL.md"
- classify_skill = skills_dir / "skills" / "fruits.classify" / "SKILL.md"
- assert meta_skill.exists()
- assert identify_skill.exists()
- assert classify_skill.exists()
-
- def test_fruits_identify_skill_content(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test the identify skill has correct content."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- generator.generate_all_skills(job, adapter, skills_dir)
-
- # Step skills use directory/SKILL.md format
- identify_skill = skills_dir / "skills" / "fruits.identify" / "SKILL.md"
- content = identify_skill.read_text()
-
- # Check header
- assert "# fruits.identify" in content
-
- # Check step info
- assert "Step 1/2" in content
-
- # Check user input is mentioned
- assert "raw_items" in content
-
- # Check output is mentioned
- assert "identified_fruits.md" in content
-
- # Check next step is suggested
- assert "/fruits.classify" in content
-
- def test_fruits_classify_skill_content(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test the classify skill has correct content."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- generator.generate_all_skills(job, adapter, skills_dir)
-
- # Step skills use directory/SKILL.md format
- classify_skill = skills_dir / "skills" / "fruits.classify" / "SKILL.md"
- content = classify_skill.read_text()
-
- # Check header
- assert "# fruits.classify" in content
-
- # Check step info
- assert "Step 2/2" in content
-
- # Check file input is mentioned
- assert "identified_fruits.md" in content
- assert "from `identify`" in content
-
- # Check output is mentioned
- assert "classified_fruits.md" in content
-
- # Check workflow complete (last step)
- assert "Workflow complete" in content
-
- def test_fruits_dependency_validation(self, fixtures_dir: Path) -> None:
- """Test that dependency validation passes for fruits job."""
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- # This should not raise - dependencies are valid
- job.validate_dependencies()
-
- def test_fruits_job_is_deterministic_design(self, fixtures_dir: Path) -> None:
- """Verify the fruits job is designed for deterministic testing.
-
- This test documents the design properties that make this job
- suitable for CI testing.
- """
- job_dir = fixtures_dir / "jobs" / "fruits"
- job = parse_job_definition(job_dir)
-
- # Job has clear, simple structure
- assert len(job.steps) == 2
-
- # Steps form a linear dependency chain
- assert job.steps[0].dependencies == []
- assert job.steps[1].dependencies == ["identify"]
-
- # First step takes user input
- identify_step = job.steps[0]
- assert len(identify_step.inputs) == 1
- assert identify_step.inputs[0].is_user_input()
-
- # Second step uses output from first step
- classify_step = job.steps[1]
- assert len(classify_step.inputs) == 1
- assert classify_step.inputs[0].is_file_input()
- assert classify_step.inputs[0].from_step == "identify"
-
- # Outputs are well-defined markdown files
- assert len(identify_step.outputs) == 1
- assert identify_step.outputs[0].file == "identified_fruits.md"
- assert len(classify_step.outputs) == 1
- assert classify_step.outputs[0].file == "classified_fruits.md"
diff --git a/tests/integration/test_full_workflow.py b/tests/integration/test_full_workflow.py
deleted file mode 100644
index bc7f83bf..00000000
--- a/tests/integration/test_full_workflow.py
+++ /dev/null
@@ -1,153 +0,0 @@
-"""Integration tests for full job workflow."""
-
-from pathlib import Path
-
-from deepwork.core.adapters import ClaudeAdapter
-from deepwork.core.generator import SkillGenerator
-from deepwork.core.parser import parse_job_definition
-
-
-class TestJobWorkflow:
- """Integration tests for complete job workflow."""
-
- def test_parse_and_generate_workflow(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test complete workflow: parse job → generate skills."""
- # Step 1: Parse job definition
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- assert job.name == "competitive_research"
- assert len(job.steps) == 4
-
- # Step 2: Generate skills
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # Now includes meta-skill + step skills
- assert len(skill_paths) == 5 # 1 meta + 4 steps
-
- # First skill is the meta-skill
- assert skill_paths[0].exists()
- meta_content = skill_paths[0].read_text()
- assert f"# {job.name}" in meta_content
- assert "Available Steps" in meta_content
-
- # Verify all step skill files exist and have correct content
- for i, skill_path in enumerate(skill_paths[1:]): # Skip meta-skill
- assert skill_path.exists()
- content = skill_path.read_text()
-
- # Check skill name format (header)
- assert f"# {job.name}.{job.steps[i].id}" in content
-
- # Check step numbers
- assert f"Step {i + 1}/4" in content
-
- def test_simple_job_workflow(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test workflow with simple single-step job."""
- # Parse
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- assert len(job.steps) == 1
-
- # Generate
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # Now includes meta-skill + step skills
- assert len(skill_paths) == 2 # 1 meta + 1 step
-
- # Verify step skill content (skip meta-skill at index 0)
- content = skill_paths[1].read_text()
- assert "# simple_job.single_step" in content
- # Single step with no dependencies is treated as standalone
- assert "Standalone skill" in content
- assert "input_param" in content
- assert "standalone skill can be re-run" in content # Standalone completion message
-
- def test_skill_generation_with_dependencies(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test that generated skills properly handle dependencies."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # skill_paths[0] is meta-skill, steps start at index 1
-
- # Check first step (no prerequisites)
- step1_content = skill_paths[1].read_text()
- assert "## Prerequisites" not in step1_content
- assert "/competitive_research.primary_research" in step1_content # Next step
-
- # Check second step (has prerequisites and next step)
- step2_content = skill_paths[2].read_text()
- assert "## Prerequisites" in step2_content
- assert "/competitive_research.identify_competitors" in step2_content
- assert "/competitive_research.secondary_research" in step2_content # Next step
-
- # Check last step (has prerequisites, no next step)
- step4_content = skill_paths[4].read_text()
- assert "## Prerequisites" in step4_content
- assert "**Workflow complete**" in step4_content
- assert "## Next Step" not in step4_content
-
- def test_skill_generation_with_file_inputs(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test that generated skills properly handle file inputs."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # skill_paths[0] is meta-skill, steps start at index 1
-
- # Check step with file input
- step2_content = skill_paths[2].read_text() # primary_research (index 2)
- assert "## Required Inputs" in step2_content
- assert "**Files from Previous Steps**" in step2_content
- assert "competitors.md" in step2_content
- assert "from `identify_competitors`" in step2_content
-
- # Check step with multiple file inputs
- step4_content = skill_paths[4].read_text() # comparative_report (index 4)
- assert "primary_research.md" in step4_content
- assert "secondary_research.md" in step4_content
-
- def test_skill_generation_with_user_inputs(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test that generated skills properly handle user parameter inputs."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
- skills_dir = temp_dir / ".claude"
- skills_dir.mkdir()
-
- skill_paths = generator.generate_all_skills(job, adapter, skills_dir)
-
- # skill_paths[0] is meta-skill, steps start at index 1
-
- # Check step with user inputs
- step1_content = skill_paths[1].read_text() # identify_competitors (index 1)
- assert "## Required Inputs" in step1_content
- assert "**User Parameters**" in step1_content
- assert "market_segment" in step1_content
- assert "product_category" in step1_content
diff --git a/tests/integration/test_install_flow.py b/tests/integration/test_install_flow.py
index d0638275..a607c725 100644
--- a/tests/integration/test_install_flow.py
+++ b/tests/integration/test_install_flow.py
@@ -30,7 +30,7 @@ def test_install_with_claude(self, mock_claude_project: Path) -> None:
# Verify directory structure
deepwork_dir = mock_claude_project / ".deepwork"
assert deepwork_dir.exists()
- assert (deepwork_dir / "jobs").exists()
+ assert (deepwork_dir / "experts").exists()
# Verify config.yml
config_file = deepwork_dir / "config.yml"
@@ -40,24 +40,23 @@ def test_install_with_claude(self, mock_claude_project: Path) -> None:
assert "claude" in config["platforms"]
# Verify core skills were created (directory/SKILL.md format)
+ # With the new workflow system, skills use hyphenated expert names
claude_dir = mock_claude_project / ".claude" / "skills"
- # Meta-skill
- assert (claude_dir / "deepwork_jobs" / "SKILL.md").exists()
- # Step skill (no prefix, but has user-invocable: false in frontmatter)
- assert (claude_dir / "deepwork_jobs.define" / "SKILL.md").exists()
+ # Workflow meta-skill (expert.workflow_name)
+ assert (claude_dir / "experts.new_workflow" / "SKILL.md").exists()
+ # Step skill (expert.step_id)
+ assert (claude_dir / "experts.define" / "SKILL.md").exists()
# Exposed step skill (user-invocable - learn has exposed: true)
- assert (claude_dir / "deepwork_jobs.learn" / "SKILL.md").exists()
+ assert (claude_dir / "experts.learn" / "SKILL.md").exists()
- # Verify meta-skill content
- meta_skill = (claude_dir / "deepwork_jobs" / "SKILL.md").read_text()
- assert "# deepwork_jobs" in meta_skill
- # deepwork_jobs has workflows defined, so it shows "Workflows" instead of "Available Steps"
- assert "Workflows" in meta_skill or "Available Steps" in meta_skill
+ # Verify workflow meta-skill content
+ meta_skill = (claude_dir / "experts.new_workflow" / "SKILL.md").read_text()
+ assert "# experts.new_workflow" in meta_skill
# Verify step skill content
- define_skill = (claude_dir / "deepwork_jobs.define" / "SKILL.md").read_text()
- assert "# deepwork_jobs.define" in define_skill
- assert "Define Job Specification" in define_skill
+ define_skill = (claude_dir / "experts.define" / "SKILL.md").read_text()
+ assert "# experts.define" in define_skill
+ assert "Define Workflow Specification" in define_skill
def test_install_with_auto_detect(self, mock_claude_project: Path) -> None:
"""Test installing with auto-detection."""
@@ -105,7 +104,7 @@ def test_install_defaults_to_claude_when_no_platform(self, mock_git_repo: Path)
# Verify skills were created for Claude
skills_dir = claude_dir / "skills"
- assert (skills_dir / "deepwork_jobs" / "SKILL.md").exists()
+ assert (skills_dir / "experts.define" / "SKILL.md").exists()
def test_install_with_multiple_platforms_auto_detect(
self, mock_multi_platform_project: Path
@@ -132,17 +131,13 @@ def test_install_with_multiple_platforms_auto_detect(
assert "claude" in config["platforms"]
assert "gemini" in config["platforms"]
- # Verify skills were created for both platforms
+ # Verify skills were created for Claude
claude_dir = mock_multi_platform_project / ".claude" / "skills"
- # Meta-skill and step skills (directory/SKILL.md format)
- assert (claude_dir / "deepwork_jobs" / "SKILL.md").exists()
- assert (claude_dir / "deepwork_jobs.define" / "SKILL.md").exists()
+ # Step skills (directory/SKILL.md format with hyphenated expert names)
+ assert (claude_dir / "experts.define" / "SKILL.md").exists()
- # Gemini uses job_name/step_id.toml structure
- gemini_dir = mock_multi_platform_project / ".gemini" / "skills"
- # Meta-skill (index.toml) and step skills
- assert (gemini_dir / "deepwork_jobs" / "index.toml").exists()
- assert (gemini_dir / "deepwork_jobs" / "define.toml").exists()
+ # Note: Gemini workflow skill generation is not yet implemented
+ # (templates need to be migrated from job format to workflow format)
def test_install_with_specified_platform_when_missing(self, mock_git_repo: Path) -> None:
"""Test that install fails when specified platform is not present."""
@@ -181,10 +176,9 @@ def test_install_is_idempotent(self, mock_claude_project: Path) -> None:
assert (deepwork_dir / "config.yml").exists()
claude_dir = mock_claude_project / ".claude" / "skills"
- # Meta-skill and step skills (directory/SKILL.md format)
- assert (claude_dir / "deepwork_jobs" / "SKILL.md").exists()
- assert (claude_dir / "deepwork_jobs.define" / "SKILL.md").exists()
- assert (claude_dir / "deepwork_jobs.learn" / "SKILL.md").exists()
+ # Step skills (directory/SKILL.md format with hyphenated expert names)
+ assert (claude_dir / "experts.define" / "SKILL.md").exists()
+ assert (claude_dir / "experts.learn" / "SKILL.md").exists()
def test_install_creates_rules_directory(self, mock_claude_project: Path) -> None:
"""Test that install creates the v2 rules directory with example templates."""
diff --git a/tests/shell_script_tests/conftest.py b/tests/shell_script_tests/conftest.py
index 3ac15822..70a662de 100644
--- a/tests/shell_script_tests/conftest.py
+++ b/tests/shell_script_tests/conftest.py
@@ -62,7 +62,8 @@ def rules_hooks_dir() -> Path:
Path(__file__).parent.parent.parent
/ "src"
/ "deepwork"
- / "standard_jobs"
+ / "standard"
+ / "experts"
/ "deepwork_rules"
/ "hooks"
)
@@ -80,14 +81,6 @@ def src_dir() -> Path:
return Path(__file__).parent.parent.parent / "src"
-@pytest.fixture
-def jobs_scripts_dir() -> Path:
- """Return the path to the jobs scripts directory."""
- return (
- Path(__file__).parent.parent.parent / "src" / "deepwork" / "standard_jobs" / "deepwork_jobs"
- )
-
-
def run_shell_script(
script_path: Path,
cwd: Path,
diff --git a/tests/shell_script_tests/test_make_new_job.py b/tests/shell_script_tests/test_make_new_job.py
deleted file mode 100644
index 913d66ea..00000000
--- a/tests/shell_script_tests/test_make_new_job.py
+++ /dev/null
@@ -1,313 +0,0 @@
-"""Tests for make_new_job.sh utility script.
-
-This script creates the directory structure for a new DeepWork job.
-It should:
-1. Validate job name format (lowercase, letters/numbers/underscores)
-2. Create the job directory structure under .deepwork/jobs/
-3. Create required subdirectories (steps/, hooks/, templates/)
-4. Create AGENTS.md with guidance
-5. Handle existing jobs gracefully (error)
-6. Handle missing .deepwork directory by creating it
-"""
-
-from pathlib import Path
-
-import pytest
-
-from .conftest import run_shell_script
-
-
-@pytest.fixture
-def project_dir(tmp_path: Path) -> Path:
- """Create a basic project directory."""
- return tmp_path
-
-
-@pytest.fixture
-def project_with_deepwork(tmp_path: Path) -> Path:
- """Create a project with existing .deepwork/jobs directory."""
- jobs_dir = tmp_path / ".deepwork" / "jobs"
- jobs_dir.mkdir(parents=True)
- return tmp_path
-
-
-def run_make_new_job(
- script_path: Path,
- cwd: Path,
- job_name: str | None = None,
-) -> tuple[str, str, int]:
- """Run the make_new_job.sh script."""
- args = [job_name] if job_name else None
- return run_shell_script(script_path, cwd, args=args, env_extra={"NO_COLOR": "1"})
-
-
-class TestMakeNewJobUsage:
- """Tests for make_new_job.sh usage and help output."""
-
- def test_shows_usage_without_arguments(self, jobs_scripts_dir: Path, project_dir: Path) -> None:
- """Test that the script shows usage when called without arguments."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_dir)
-
- assert code == 1, "Should exit with error when no arguments"
- assert "Usage:" in stdout, "Should show usage information"
- assert "job_name" in stdout.lower(), "Should mention job_name argument"
-
- def test_shows_example_in_usage(self, jobs_scripts_dir: Path, project_dir: Path) -> None:
- """Test that the usage includes an example."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_dir)
-
- assert "Example:" in stdout, "Should show example usage"
-
-
-class TestMakeNewJobNameValidation:
- """Tests for job name validation in make_new_job.sh."""
-
- def test_accepts_lowercase_name(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that lowercase names are accepted."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "valid_job")
-
- assert code == 0, f"Should accept lowercase name. stderr: {stderr}"
-
- def test_accepts_name_with_numbers(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that names with numbers are accepted."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "job123")
-
- assert code == 0, f"Should accept name with numbers. stderr: {stderr}"
-
- def test_accepts_name_with_underscores(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that names with underscores are accepted."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "my_new_job")
-
- assert code == 0, f"Should accept underscores. stderr: {stderr}"
-
- def test_rejects_uppercase_name(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that uppercase names are rejected."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "InvalidJob")
-
- assert code != 0, "Should reject uppercase name"
- # Check for error message in stdout (script uses echo)
- output = stdout + stderr
- assert "invalid" in output.lower() or "error" in output.lower(), (
- "Should show error for invalid name"
- )
-
- def test_rejects_name_starting_with_number(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that names starting with numbers are rejected."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "123job")
-
- assert code != 0, "Should reject name starting with number"
-
- def test_rejects_name_with_hyphens(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that names with hyphens are rejected."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "my-job")
-
- assert code != 0, "Should reject name with hyphens"
-
- def test_rejects_name_with_spaces(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that names with spaces are rejected."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- # This will be passed as two arguments by bash, causing an error
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "my job")
-
- # Either fails validation or treats "job" as separate (job is valid name)
- # The key is it shouldn't create "my job" as a directory name
- bad_dir = project_with_deepwork / ".deepwork" / "jobs" / "my job"
- assert not bad_dir.exists(), "Should not create directory with space in name"
-
-
-class TestMakeNewJobDirectoryStructure:
- """Tests for directory structure creation in make_new_job.sh."""
-
- def test_creates_main_job_directory(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that the main job directory is created."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- job_dir = project_with_deepwork / ".deepwork" / "jobs" / "test_job"
- assert job_dir.exists(), "Job directory should be created"
- assert job_dir.is_dir(), "Job path should be a directory"
-
- def test_creates_steps_directory(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that steps/ subdirectory is created."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- steps_dir = project_with_deepwork / ".deepwork" / "jobs" / "test_job" / "steps"
- assert steps_dir.exists(), "steps/ directory should be created"
- assert steps_dir.is_dir(), "steps/ should be a directory"
-
- def test_creates_hooks_directory(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that hooks/ subdirectory is created."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- hooks_dir = project_with_deepwork / ".deepwork" / "jobs" / "test_job" / "hooks"
- assert hooks_dir.exists(), "hooks/ directory should be created"
- assert hooks_dir.is_dir(), "hooks/ should be a directory"
-
- def test_creates_templates_directory(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that templates/ subdirectory is created."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- templates_dir = project_with_deepwork / ".deepwork" / "jobs" / "test_job" / "templates"
- assert templates_dir.exists(), "templates/ directory should be created"
- assert templates_dir.is_dir(), "templates/ should be a directory"
-
- def test_creates_gitkeep_files(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that .gitkeep files are created in empty directories."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- job_dir = project_with_deepwork / ".deepwork" / "jobs" / "test_job"
-
- hooks_gitkeep = job_dir / "hooks" / ".gitkeep"
- templates_gitkeep = job_dir / "templates" / ".gitkeep"
-
- assert hooks_gitkeep.exists(), "hooks/.gitkeep should be created"
- assert templates_gitkeep.exists(), "templates/.gitkeep should be created"
-
- def test_creates_agents_md(self, jobs_scripts_dir: Path, project_with_deepwork: Path) -> None:
- """Test that AGENTS.md file is created."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- agents_md = project_with_deepwork / ".deepwork" / "jobs" / "test_job" / "AGENTS.md"
- assert agents_md.exists(), "AGENTS.md should be created"
-
- content = agents_md.read_text()
- assert "Job Management" in content, "AGENTS.md should have job management content"
- assert "deepwork_jobs" in content, "AGENTS.md should reference deepwork_jobs"
-
-
-class TestMakeNewJobAgentsMdContent:
- """Tests for AGENTS.md content in make_new_job.sh."""
-
- def test_agents_md_contains_slash_commands(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that AGENTS.md lists recommended slash commands."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- agents_md = project_with_deepwork / ".deepwork" / "jobs" / "test_job" / "AGENTS.md"
- content = agents_md.read_text()
-
- assert "/deepwork_jobs.define" in content, "Should mention define command"
- assert "/deepwork_jobs.implement" in content, "Should mention implement command"
- assert "/deepwork_jobs.learn" in content, "Should mention learn command"
-
- def test_agents_md_contains_directory_structure(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that AGENTS.md documents the directory structure."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "test_job")
-
- agents_md = project_with_deepwork / ".deepwork" / "jobs" / "test_job" / "AGENTS.md"
- content = agents_md.read_text()
-
- assert "job.yml" in content, "Should mention job.yml"
- assert "steps/" in content, "Should document steps directory"
- assert "hooks/" in content, "Should document hooks directory"
- assert "templates/" in content, "Should document templates directory"
-
-
-class TestMakeNewJobErrorHandling:
- """Tests for error handling in make_new_job.sh."""
-
- def test_fails_if_job_already_exists(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that creating a job that already exists fails."""
- # First create the job
- script_path = jobs_scripts_dir / "make_new_job.sh"
- run_make_new_job(script_path, project_with_deepwork, "existing_job")
-
- # Try to create it again
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "existing_job")
-
- assert code != 0, "Should fail when job already exists"
- output = stdout + stderr
- assert "exist" in output.lower() or "error" in output.lower(), (
- "Should mention that job exists"
- )
-
- def test_creates_deepwork_directory_if_missing(
- self, jobs_scripts_dir: Path, project_dir: Path
- ) -> None:
- """Test that .deepwork/jobs is created if it doesn't exist."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_dir, "new_job")
-
- assert code == 0, f"Should succeed even without .deepwork. stderr: {stderr}"
-
- job_dir = project_dir / ".deepwork" / "jobs" / "new_job"
- assert job_dir.exists(), "Should create .deepwork/jobs/new_job"
-
-
-class TestMakeNewJobOutput:
- """Tests for output messages in make_new_job.sh."""
-
- def test_shows_success_message(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that success message is shown."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "new_job")
-
- assert code == 0, f"Should succeed. stderr: {stderr}"
- # Check for informational output
- assert "new_job" in stdout, "Output should mention job name"
-
- def test_shows_next_steps(self, jobs_scripts_dir: Path, project_with_deepwork: Path) -> None:
- """Test that next steps are shown after creation."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "new_job")
-
- assert code == 0, f"Should succeed. stderr: {stderr}"
- # Should mention what to do next
- assert "next" in stdout.lower() or "step" in stdout.lower(), "Should show next steps"
-
- def test_shows_directory_structure_created(
- self, jobs_scripts_dir: Path, project_with_deepwork: Path
- ) -> None:
- """Test that created directory structure is shown."""
- script_path = jobs_scripts_dir / "make_new_job.sh"
- stdout, stderr, code = run_make_new_job(script_path, project_with_deepwork, "new_job")
-
- assert code == 0, f"Should succeed. stderr: {stderr}"
- # Should show what was created
- assert "AGENTS.md" in stdout or "steps" in stdout, "Should show created structure"
diff --git a/tests/unit/test_adapters.py b/tests/unit/test_adapters.py
index 18e8d6d7..ce368227 100644
--- a/tests/unit/test_adapters.py
+++ b/tests/unit/test_adapters.py
@@ -374,7 +374,7 @@ def test_class_attributes(self) -> None:
assert GeminiAdapter.display_name == "Gemini CLI"
assert GeminiAdapter.config_dir == ".gemini"
assert GeminiAdapter.skills_dir == "skills"
- assert GeminiAdapter.skill_template == "skill-job-step.toml.jinja"
+ assert GeminiAdapter.skill_template == "skill-workflow-step.toml.jinja"
def test_init_with_project_root(self, temp_dir: Path) -> None:
"""Test initialization with project root."""
diff --git a/tests/unit/test_expert_schema.py b/tests/unit/test_expert_schema.py
new file mode 100644
index 00000000..506b888c
--- /dev/null
+++ b/tests/unit/test_expert_schema.py
@@ -0,0 +1,216 @@
+"""Tests for expert schema validation."""
+
+import pytest
+
+from deepwork.schemas.expert_schema import (
+ EXPERT_SCHEMA,
+ LEARNING_FRONTMATTER_SCHEMA,
+ TOPIC_FRONTMATTER_SCHEMA,
+)
+from deepwork.utils.validation import ValidationError, validate_against_schema
+
+
+class TestExpertSchema:
+ """Tests for expert.yml schema validation."""
+
+ def test_valid_minimal_expert(self) -> None:
+ """Test valid minimal expert definition."""
+ data = {
+ "discovery_description": "Test expert for unit testing",
+ "full_expertise": "You are an expert on testing.",
+ }
+ # Should not raise
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+ def test_valid_multiline_expert(self) -> None:
+ """Test valid expert with multiline content."""
+ data = {
+ "discovery_description": "Test expert for unit testing\nwith multiple lines",
+ "full_expertise": "# Test Expert\n\nYou are an expert on testing.\n\n## Topics\n\n- Testing basics\n- Advanced testing",
+ }
+ # Should not raise
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+ def test_missing_discovery_description(self) -> None:
+ """Test that missing discovery_description fails validation."""
+ data = {
+ "full_expertise": "You are an expert on testing.",
+ }
+ with pytest.raises(ValidationError, match="discovery_description"):
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+ def test_missing_full_expertise(self) -> None:
+ """Test that missing full_expertise fails validation."""
+ data = {
+ "discovery_description": "Test expert",
+ }
+ with pytest.raises(ValidationError, match="full_expertise"):
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+ def test_empty_discovery_description(self) -> None:
+ """Test that empty discovery_description fails validation."""
+ data = {
+ "discovery_description": "",
+ "full_expertise": "You are an expert on testing.",
+ }
+ with pytest.raises(ValidationError):
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+ def test_empty_full_expertise(self) -> None:
+ """Test that empty full_expertise fails validation."""
+ data = {
+ "discovery_description": "Test expert",
+ "full_expertise": "",
+ }
+ with pytest.raises(ValidationError):
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+ def test_additional_properties_not_allowed(self) -> None:
+ """Test that additional properties are not allowed."""
+ data = {
+ "discovery_description": "Test expert",
+ "full_expertise": "You are an expert on testing.",
+ "extra_field": "not allowed",
+ }
+ with pytest.raises(ValidationError, match="extra_field"):
+ validate_against_schema(data, EXPERT_SCHEMA)
+
+
+class TestTopicFrontmatterSchema:
+ """Tests for topic frontmatter schema validation."""
+
+ def test_valid_minimal_topic(self) -> None:
+ """Test valid minimal topic with just name."""
+ data = {"name": "Test Topic"}
+ # Should not raise
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_valid_full_topic(self) -> None:
+ """Test valid topic with all fields."""
+ data = {
+ "name": "Test Topic",
+ "keywords": ["testing", "unit test", "pytest"],
+ "last_updated": "2025-01-30",
+ }
+ # Should not raise
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_missing_name(self) -> None:
+ """Test that missing name fails validation."""
+ data = {
+ "keywords": ["testing"],
+ }
+ with pytest.raises(ValidationError, match="name"):
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_empty_name(self) -> None:
+ """Test that empty name fails validation."""
+ data = {"name": ""}
+ with pytest.raises(ValidationError):
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_invalid_date_format(self) -> None:
+ """Test that invalid date format fails validation."""
+ data = {
+ "name": "Test Topic",
+ "last_updated": "January 30, 2025",
+ }
+ with pytest.raises(ValidationError, match="last_updated"):
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_valid_date_format(self) -> None:
+ """Test that valid YYYY-MM-DD date format passes."""
+ data = {
+ "name": "Test Topic",
+ "last_updated": "2025-01-30",
+ }
+ # Should not raise
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_empty_keywords_list(self) -> None:
+ """Test that empty keywords list is allowed."""
+ data = {
+ "name": "Test Topic",
+ "keywords": [],
+ }
+ # Should not raise
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+ def test_additional_properties_not_allowed(self) -> None:
+ """Test that additional properties are not allowed."""
+ data = {
+ "name": "Test Topic",
+ "extra": "not allowed",
+ }
+ with pytest.raises(ValidationError, match="extra"):
+ validate_against_schema(data, TOPIC_FRONTMATTER_SCHEMA)
+
+
+class TestLearningFrontmatterSchema:
+ """Tests for learning frontmatter schema validation."""
+
+ def test_valid_minimal_learning(self) -> None:
+ """Test valid minimal learning with just name."""
+ data = {"name": "Test Learning"}
+ # Should not raise
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_valid_full_learning(self) -> None:
+ """Test valid learning with all fields."""
+ data = {
+ "name": "Test Learning",
+ "last_updated": "2025-01-30",
+ "summarized_result": "Discovered that X causes Y under Z conditions.",
+ }
+ # Should not raise
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_missing_name(self) -> None:
+ """Test that missing name fails validation."""
+ data = {
+ "summarized_result": "Some finding",
+ }
+ with pytest.raises(ValidationError, match="name"):
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_empty_name(self) -> None:
+ """Test that empty name fails validation."""
+ data = {"name": ""}
+ with pytest.raises(ValidationError):
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_empty_summarized_result(self) -> None:
+ """Test that empty summarized_result fails validation."""
+ data = {
+ "name": "Test Learning",
+ "summarized_result": "",
+ }
+ with pytest.raises(ValidationError):
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_invalid_date_format(self) -> None:
+ """Test that invalid date format fails validation."""
+ data = {
+ "name": "Test Learning",
+ "last_updated": "30-01-2025",
+ }
+ with pytest.raises(ValidationError, match="last_updated"):
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_multiline_summarized_result(self) -> None:
+ """Test that multiline summarized_result is allowed."""
+ data = {
+ "name": "Test Learning",
+ "summarized_result": "First line\nSecond line\nThird line",
+ }
+ # Should not raise
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
+
+ def test_additional_properties_not_allowed(self) -> None:
+ """Test that additional properties are not allowed."""
+ data = {
+ "name": "Test Learning",
+ "extra": "not allowed",
+ }
+ with pytest.raises(ValidationError, match="extra"):
+ validate_against_schema(data, LEARNING_FRONTMATTER_SCHEMA)
diff --git a/tests/unit/test_experts_cli.py b/tests/unit/test_experts_cli.py
new file mode 100644
index 00000000..16ba9b47
--- /dev/null
+++ b/tests/unit/test_experts_cli.py
@@ -0,0 +1,288 @@
+"""Tests for expert CLI commands."""
+
+from pathlib import Path
+
+import pytest
+from click.testing import CliRunner
+
+from deepwork.cli.experts import learnings, topics
+
+
+@pytest.fixture
+def runner() -> CliRunner:
+ """Create a Click test runner."""
+ return CliRunner()
+
+
+@pytest.fixture
+def project_with_expert(tmp_path: Path) -> Path:
+ """Create a project with an expert."""
+ # Create .deepwork/experts directory
+ experts_dir = tmp_path / ".deepwork" / "experts"
+ experts_dir.mkdir(parents=True)
+
+ # Create an expert
+ expert_dir = experts_dir / "test_expert"
+ expert_dir.mkdir()
+
+ # Create expert.yml
+ (expert_dir / "expert.yml").write_text(
+ """discovery_description: |
+ Test expert for CLI testing
+
+full_expertise: |
+ You are an expert on testing CLI commands.
+"""
+ )
+
+ # Create topics directory with topics
+ topics_dir = expert_dir / "topics"
+ topics_dir.mkdir()
+
+ (topics_dir / "basics.md").write_text(
+ """---
+name: Testing Basics
+keywords:
+ - testing
+ - basics
+last_updated: 2025-01-30
+---
+
+Content about testing basics.
+"""
+ )
+
+ (topics_dir / "advanced.md").write_text(
+ """---
+name: Advanced Testing
+keywords:
+ - advanced
+ - mocking
+last_updated: 2025-01-15
+---
+
+Content about advanced testing.
+"""
+ )
+
+ # Create learnings directory with learnings
+ learnings_dir = expert_dir / "learnings"
+ learnings_dir.mkdir()
+
+ (learnings_dir / "discovery.md").write_text(
+ """---
+name: Important Discovery
+last_updated: 2025-01-20
+summarized_result: |
+ Found that X causes Y under Z conditions.
+---
+
+Full details of the discovery.
+"""
+ )
+
+ return tmp_path
+
+
+class TestTopicsCommand:
+ """Tests for the topics command."""
+
+ def test_topics_command_help(self, runner: CliRunner) -> None:
+ """Test topics command shows help."""
+ result = runner.invoke(topics, ["--help"])
+
+ assert result.exit_code == 0
+ assert "List topics for an expert" in result.output
+ assert "--expert" in result.output
+
+ def test_topics_command_requires_expert(self, runner: CliRunner) -> None:
+ """Test topics command requires --expert option."""
+ result = runner.invoke(topics, [])
+
+ assert result.exit_code != 0
+ assert "Missing option" in result.output or "required" in result.output.lower()
+
+ def test_topics_command_lists_topics(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test topics command lists topics for an expert."""
+ result = runner.invoke(
+ topics, ["--expert", "test-expert", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code == 0
+ assert "Testing Basics" in result.output
+ assert "Advanced Testing" in result.output
+ assert "testing" in result.output # keyword
+ assert "advanced" in result.output # keyword
+
+ def test_topics_command_markdown_format(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test topics command outputs markdown format."""
+ result = runner.invoke(
+ topics, ["--expert", "test-expert", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code == 0
+ # Should contain markdown links
+ assert "[Testing Basics]" in result.output
+ assert "(topics/" in result.output
+
+ def test_topics_command_sorted_by_date(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test topics are sorted by most recently updated."""
+ result = runner.invoke(
+ topics, ["--expert", "test-expert", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code == 0
+ # Testing Basics (2025-01-30) should come before Advanced Testing (2025-01-15)
+ basics_pos = result.output.find("Testing Basics")
+ advanced_pos = result.output.find("Advanced Testing")
+ assert basics_pos < advanced_pos
+
+ def test_topics_command_expert_not_found(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test topics command with nonexistent expert."""
+ result = runner.invoke(
+ topics, ["--expert", "nonexistent", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code != 0
+ assert "not found" in result.output.lower()
+
+ def test_topics_command_no_experts_dir(self, runner: CliRunner, tmp_path: Path) -> None:
+ """Test topics command when no experts directory exists."""
+ result = runner.invoke(topics, ["--expert", "test", "--path", str(tmp_path)])
+
+ assert result.exit_code != 0
+ assert "No experts directory" in result.output or "not found" in result.output.lower()
+
+ def test_topics_command_no_topics(self, runner: CliRunner, tmp_path: Path) -> None:
+ """Test topics command when expert has no topics."""
+ # Create expert without topics
+ experts_dir = tmp_path / ".deepwork" / "experts" / "empty_expert"
+ experts_dir.mkdir(parents=True)
+ (experts_dir / "expert.yml").write_text(
+ "discovery_description: Empty expert\nfull_expertise: Nothing here."
+ )
+
+ result = runner.invoke(topics, ["--expert", "empty-expert", "--path", str(tmp_path)])
+
+ assert result.exit_code == 0
+ assert "No topics yet" in result.output
+
+
+class TestLearningsCommand:
+ """Tests for the learnings command."""
+
+ def test_learnings_command_help(self, runner: CliRunner) -> None:
+ """Test learnings command shows help."""
+ result = runner.invoke(learnings, ["--help"])
+
+ assert result.exit_code == 0
+ assert "List learnings for an expert" in result.output
+ assert "--expert" in result.output
+
+ def test_learnings_command_requires_expert(self, runner: CliRunner) -> None:
+ """Test learnings command requires --expert option."""
+ result = runner.invoke(learnings, [])
+
+ assert result.exit_code != 0
+
+ def test_learnings_command_lists_learnings(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test learnings command lists learnings for an expert."""
+ result = runner.invoke(
+ learnings, ["--expert", "test-expert", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code == 0
+ assert "Important Discovery" in result.output
+ assert "X causes Y" in result.output
+
+ def test_learnings_command_markdown_format(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test learnings command outputs markdown format."""
+ result = runner.invoke(
+ learnings, ["--expert", "test-expert", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code == 0
+ # Should contain markdown links
+ assert "[Important Discovery]" in result.output
+ assert "(learnings/" in result.output
+
+ def test_learnings_command_expert_not_found(
+ self, runner: CliRunner, project_with_expert: Path
+ ) -> None:
+ """Test learnings command with nonexistent expert."""
+ result = runner.invoke(
+ learnings, ["--expert", "nonexistent", "--path", str(project_with_expert)]
+ )
+
+ assert result.exit_code != 0
+ assert "not found" in result.output.lower()
+
+ def test_learnings_command_no_learnings(self, runner: CliRunner, tmp_path: Path) -> None:
+ """Test learnings command when expert has no learnings."""
+ # Create expert without learnings
+ experts_dir = tmp_path / ".deepwork" / "experts" / "empty_expert"
+ experts_dir.mkdir(parents=True)
+ (experts_dir / "expert.yml").write_text(
+ "discovery_description: Empty expert\nfull_expertise: Nothing here."
+ )
+
+ result = runner.invoke(learnings, ["--expert", "empty-expert", "--path", str(tmp_path)])
+
+ assert result.exit_code == 0
+ assert "No learnings yet" in result.output
+
+
+class TestExpertNameResolution:
+ """Tests for expert name resolution (dashes vs underscores)."""
+
+ def test_expert_with_underscores_resolved(self, runner: CliRunner, tmp_path: Path) -> None:
+ """Test that expert-name resolves to expert_name folder."""
+ # Create expert with underscore folder name
+ experts_dir = tmp_path / ".deepwork" / "experts" / "rails_activejob"
+ experts_dir.mkdir(parents=True)
+ (experts_dir / "expert.yml").write_text(
+ "discovery_description: Rails ActiveJob\nfull_expertise: Content."
+ )
+
+ # Topics directory
+ topics_dir = experts_dir / "topics"
+ topics_dir.mkdir()
+ (topics_dir / "test.md").write_text("---\nname: Test\n---\nContent")
+
+ # Query with dashes
+ result = runner.invoke(topics, ["--expert", "rails-activejob", "--path", str(tmp_path)])
+
+ assert result.exit_code == 0
+ assert "Test" in result.output
+
+ def test_available_experts_shown_on_error(self, runner: CliRunner, tmp_path: Path) -> None:
+ """Test that available experts are listed when expert not found."""
+ # Create some experts
+ experts_dir = tmp_path / ".deepwork" / "experts"
+ experts_dir.mkdir(parents=True)
+
+ for name in ["expert_a", "expert_b"]:
+ ed = experts_dir / name
+ ed.mkdir()
+ (ed / "expert.yml").write_text(
+ f"discovery_description: {name}\nfull_expertise: Content."
+ )
+
+ # Query nonexistent expert
+ result = runner.invoke(topics, ["--expert", "nonexistent", "--path", str(tmp_path)])
+
+ assert result.exit_code != 0
+ assert "expert-a" in result.output.lower() or "expert_a" in result.output.lower()
+ assert "expert-b" in result.output.lower() or "expert_b" in result.output.lower()
diff --git a/tests/unit/test_experts_generator.py b/tests/unit/test_experts_generator.py
new file mode 100644
index 00000000..c016278c
--- /dev/null
+++ b/tests/unit/test_experts_generator.py
@@ -0,0 +1,278 @@
+"""Tests for expert agent generator."""
+
+from pathlib import Path
+
+import pytest
+
+from deepwork.core.adapters import ClaudeAdapter
+from deepwork.core.experts_generator import ExpertGenerator, ExpertGeneratorError
+from deepwork.core.experts_parser import ExpertDefinition, Learning, Topic
+
+
+class TestExpertGenerator:
+ """Tests for ExpertGenerator class."""
+
+ def test_init_default_templates(self) -> None:
+ """Test initialization with default templates directory."""
+ generator = ExpertGenerator()
+
+ assert generator.templates_dir.exists()
+ assert (generator.templates_dir / "claude").exists()
+
+ def test_init_custom_templates(self, tmp_path: Path) -> None:
+ """Test initialization with custom templates directory."""
+ templates_dir = tmp_path / "templates"
+ templates_dir.mkdir()
+
+ generator = ExpertGenerator(templates_dir)
+
+ assert generator.templates_dir == templates_dir
+
+ def test_init_nonexistent_templates(self, tmp_path: Path) -> None:
+ """Test initialization with nonexistent templates fails."""
+ with pytest.raises(ExpertGeneratorError, match="not found"):
+ ExpertGenerator(tmp_path / "nonexistent")
+
+ def test_get_agent_filename(self) -> None:
+ """Test agent filename generation."""
+ generator = ExpertGenerator()
+
+ assert generator.get_agent_filename("rails-activejob") == "dwe_rails-activejob.md"
+ assert generator.get_agent_filename("experts") == "dwe_experts.md"
+
+ def test_get_agent_name(self) -> None:
+ """Test agent name generation."""
+ generator = ExpertGenerator()
+
+ assert generator.get_agent_name("rails-activejob") == "rails-activejob"
+ assert generator.get_agent_name("experts") == "experts"
+
+
+class TestGenerateExpertAgent:
+ """Tests for generating expert agent files."""
+
+ @pytest.fixture
+ def generator(self) -> ExpertGenerator:
+ """Create a generator instance."""
+ return ExpertGenerator()
+
+ @pytest.fixture
+ def claude_adapter(self, tmp_path: Path) -> ClaudeAdapter:
+ """Create a Claude adapter."""
+ return ClaudeAdapter(project_root=tmp_path)
+
+ @pytest.fixture
+ def sample_expert(self, tmp_path: Path) -> ExpertDefinition:
+ """Create a sample expert definition."""
+ expert_dir = tmp_path / "rails_activejob"
+ expert_dir.mkdir(parents=True)
+
+ return ExpertDefinition(
+ name="rails-activejob",
+ discovery_description="Rails ActiveJob background processing",
+ full_expertise="You are an expert on Rails ActiveJob.\n\n## Key Concepts\n\n- Queues\n- Retries",
+ expert_dir=expert_dir,
+ topics=[
+ Topic(
+ name="Retry Handling",
+ keywords=["retry"],
+ source_file=expert_dir / "topics/retry.md",
+ ),
+ ],
+ learnings=[
+ Learning(
+ name="Sentry Issue",
+ summarized_result="Fixed it",
+ source_file=expert_dir / "learnings/sentry.md",
+ ),
+ ],
+ )
+
+ def test_generate_expert_agent_creates_file(
+ self,
+ generator: ExpertGenerator,
+ claude_adapter: ClaudeAdapter,
+ sample_expert: ExpertDefinition,
+ tmp_path: Path,
+ ) -> None:
+ """Test that generating an expert agent creates the file."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ agent_path = generator.generate_expert_agent(sample_expert, claude_adapter, output_dir)
+
+ assert agent_path.exists()
+ assert agent_path.name == "dwe_rails-activejob.md"
+ assert agent_path.parent.name == "agents"
+
+ def test_generate_expert_agent_content(
+ self,
+ generator: ExpertGenerator,
+ claude_adapter: ClaudeAdapter,
+ sample_expert: ExpertDefinition,
+ tmp_path: Path,
+ ) -> None:
+ """Test the content of generated expert agent file."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ agent_path = generator.generate_expert_agent(sample_expert, claude_adapter, output_dir)
+ content = agent_path.read_text()
+
+ # Check frontmatter
+ assert "name: rails-activejob" in content
+ assert "Rails ActiveJob" in content
+
+ # Check full_expertise is included
+ assert "expert on Rails ActiveJob" in content
+ assert "Key Concepts" in content
+
+ # Check dynamic command embedding
+ assert '$(deepwork topics --expert "rails-activejob")' in content
+ assert '$(deepwork learnings --expert "rails-activejob")' in content
+
+ def test_generate_expert_agent_creates_agents_dir(
+ self,
+ generator: ExpertGenerator,
+ claude_adapter: ClaudeAdapter,
+ sample_expert: ExpertDefinition,
+ tmp_path: Path,
+ ) -> None:
+ """Test that generating creates the agents directory if needed."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ assert not (output_dir / "agents").exists()
+
+ generator.generate_expert_agent(sample_expert, claude_adapter, output_dir)
+
+ assert (output_dir / "agents").exists()
+
+ def test_generate_all_expert_agents(
+ self, generator: ExpertGenerator, claude_adapter: ClaudeAdapter, tmp_path: Path
+ ) -> None:
+ """Test generating agents for multiple experts."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ expert1 = ExpertDefinition(
+ name="expert-one",
+ discovery_description="First expert",
+ full_expertise="Expert one content",
+ expert_dir=tmp_path / "expert_one",
+ )
+ expert2 = ExpertDefinition(
+ name="expert-two",
+ discovery_description="Second expert",
+ full_expertise="Expert two content",
+ expert_dir=tmp_path / "expert_two",
+ )
+
+ agent_paths = generator.generate_all_expert_agents(
+ [expert1, expert2], claude_adapter, output_dir
+ )
+
+ assert len(agent_paths) == 2
+ assert all(p.exists() for p in agent_paths)
+ assert {p.name for p in agent_paths} == {"dwe_expert-one.md", "dwe_expert-two.md"}
+
+ def test_generate_empty_experts_list(
+ self, generator: ExpertGenerator, claude_adapter: ClaudeAdapter, tmp_path: Path
+ ) -> None:
+ """Test generating with empty experts list."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ agent_paths = generator.generate_all_expert_agents([], claude_adapter, output_dir)
+
+ assert agent_paths == []
+
+
+class TestExpertAgentTemplate:
+ """Tests for the expert agent template structure."""
+
+ @pytest.fixture
+ def generator(self) -> ExpertGenerator:
+ """Create a generator instance."""
+ return ExpertGenerator()
+
+ @pytest.fixture
+ def claude_adapter(self, tmp_path: Path) -> ClaudeAdapter:
+ """Create a Claude adapter."""
+ return ClaudeAdapter(project_root=tmp_path)
+
+ def test_template_has_yaml_frontmatter(
+ self, generator: ExpertGenerator, claude_adapter: ClaudeAdapter, tmp_path: Path
+ ) -> None:
+ """Test that generated agent has YAML frontmatter."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ expert = ExpertDefinition(
+ name="test-expert",
+ discovery_description="Test description",
+ full_expertise="Test expertise",
+ expert_dir=tmp_path / "test_expert",
+ )
+
+ agent_path = generator.generate_expert_agent(expert, claude_adapter, output_dir)
+ content = agent_path.read_text()
+
+ # Check YAML frontmatter markers
+ assert content.startswith("---\n")
+ lines = content.split("\n")
+ # Find second ---
+ second_marker = None
+ for i, line in enumerate(lines[1:], 1):
+ if line == "---":
+ second_marker = i
+ break
+ assert second_marker is not None, "YAML frontmatter not properly closed"
+
+ def test_template_escapes_description(
+ self, generator: ExpertGenerator, claude_adapter: ClaudeAdapter, tmp_path: Path
+ ) -> None:
+ """Test that description with quotes is properly escaped."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ expert = ExpertDefinition(
+ name="test-expert",
+ discovery_description='Description with "quotes" inside',
+ full_expertise="Test expertise",
+ expert_dir=tmp_path / "test_expert",
+ )
+
+ agent_path = generator.generate_expert_agent(expert, claude_adapter, output_dir)
+ content = agent_path.read_text()
+
+ # Should escape quotes in description
+ assert r"\"quotes\"" in content or "quotes" in content
+
+ def test_template_truncates_long_description(
+ self, generator: ExpertGenerator, claude_adapter: ClaudeAdapter, tmp_path: Path
+ ) -> None:
+ """Test that long descriptions are truncated."""
+ output_dir = tmp_path / ".claude"
+ output_dir.mkdir()
+
+ # Create a very long description
+ long_description = "A" * 500
+
+ expert = ExpertDefinition(
+ name="test-expert",
+ discovery_description=long_description,
+ full_expertise="Test expertise",
+ expert_dir=tmp_path / "test_expert",
+ )
+
+ agent_path = generator.generate_expert_agent(expert, claude_adapter, output_dir)
+ content = agent_path.read_text()
+
+ # Extract description from frontmatter
+ lines = content.split("\n")
+ for line in lines:
+ if line.startswith("description:"):
+ # Should be truncated
+ assert len(line) < 500
+ break
diff --git a/tests/unit/test_experts_parser.py b/tests/unit/test_experts_parser.py
new file mode 100644
index 00000000..a8e89c4b
--- /dev/null
+++ b/tests/unit/test_experts_parser.py
@@ -0,0 +1,563 @@
+"""Tests for expert definition parser."""
+
+from datetime import date
+from pathlib import Path
+
+import pytest
+
+from deepwork.core.experts_parser import (
+ ExpertDefinition,
+ ExpertParseError,
+ Learning,
+ Topic,
+ _folder_name_to_expert_name,
+ discover_experts,
+ format_learnings_markdown,
+ format_topics_markdown,
+ parse_expert_definition,
+ parse_learning_file,
+ parse_topic_file,
+)
+
+
+class TestFolderNameConversion:
+ """Tests for folder name to expert name conversion."""
+
+ def test_underscores_to_dashes(self) -> None:
+ """Test that underscores are converted to dashes."""
+ assert _folder_name_to_expert_name("rails_activejob") == "rails-activejob"
+
+ def test_spaces_to_dashes(self) -> None:
+ """Test that spaces are converted to dashes."""
+ assert _folder_name_to_expert_name("rails activejob") == "rails-activejob"
+
+ def test_mixed_conversion(self) -> None:
+ """Test mixed underscores and spaces."""
+ assert _folder_name_to_expert_name("rails_active job") == "rails-active-job"
+
+ def test_no_conversion_needed(self) -> None:
+ """Test name that doesn't need conversion."""
+ assert _folder_name_to_expert_name("experts") == "experts"
+
+ def test_already_dashes(self) -> None:
+ """Test name that already has dashes."""
+ assert _folder_name_to_expert_name("rails-activejob") == "rails-activejob"
+
+
+class TestTopic:
+ """Tests for Topic dataclass."""
+
+ def test_from_dict_minimal(self) -> None:
+ """Test creating topic from minimal dict."""
+ data = {"name": "Test Topic"}
+ topic = Topic.from_dict(data)
+
+ assert topic.name == "Test Topic"
+ assert topic.keywords == []
+ assert topic.last_updated is None
+ assert topic.body == ""
+
+ def test_from_dict_full(self) -> None:
+ """Test creating topic from full dict."""
+ data = {
+ "name": "Retry Handling",
+ "keywords": ["retry", "backoff"],
+ "last_updated": "2025-01-30",
+ }
+ topic = Topic.from_dict(data, body="Content here", source_file=Path("topics/retry.md"))
+
+ assert topic.name == "Retry Handling"
+ assert topic.keywords == ["retry", "backoff"]
+ assert topic.last_updated == date(2025, 1, 30)
+ assert topic.body == "Content here"
+ assert topic.source_file == Path("topics/retry.md")
+
+ def test_relative_path(self) -> None:
+ """Test relative path generation."""
+ topic = Topic(name="Test", source_file=Path("/some/path/topics/test.md"))
+ assert topic.relative_path == "topics/test.md"
+
+ def test_relative_path_no_source(self) -> None:
+ """Test relative path when no source file."""
+ topic = Topic(name="Test")
+ assert topic.relative_path is None
+
+
+class TestLearning:
+ """Tests for Learning dataclass."""
+
+ def test_from_dict_minimal(self) -> None:
+ """Test creating learning from minimal dict."""
+ data = {"name": "Test Learning"}
+ learning = Learning.from_dict(data)
+
+ assert learning.name == "Test Learning"
+ assert learning.last_updated is None
+ assert learning.summarized_result is None
+ assert learning.body == ""
+
+ def test_from_dict_full(self) -> None:
+ """Test creating learning from full dict."""
+ data = {
+ "name": "Job errors not going to Sentry",
+ "last_updated": "2025-01-20",
+ "summarized_result": "Sentry changed their gem.",
+ }
+ learning = Learning.from_dict(
+ data, body="Full content", source_file=Path("learnings/sentry.md")
+ )
+
+ assert learning.name == "Job errors not going to Sentry"
+ assert learning.last_updated == date(2025, 1, 20)
+ assert learning.summarized_result == "Sentry changed their gem."
+ assert learning.body == "Full content"
+ assert learning.source_file == Path("learnings/sentry.md")
+
+ def test_relative_path(self) -> None:
+ """Test relative path generation."""
+ learning = Learning(name="Test", source_file=Path("/some/path/learnings/test.md"))
+ assert learning.relative_path == "learnings/test.md"
+
+
+class TestExpertDefinition:
+ """Tests for ExpertDefinition dataclass."""
+
+ def test_from_dict(self) -> None:
+ """Test creating expert definition from dict."""
+ data = {
+ "discovery_description": "Test expert",
+ "full_expertise": "You are an expert.",
+ }
+ expert = ExpertDefinition.from_dict(data, Path("/path/to/rails_activejob"))
+
+ assert expert.name == "rails-activejob"
+ assert expert.discovery_description == "Test expert"
+ assert expert.full_expertise == "You are an expert."
+ assert expert.expert_dir == Path("/path/to/rails_activejob")
+ assert expert.topics == []
+ assert expert.learnings == []
+
+ def test_from_dict_with_topics_and_learnings(self) -> None:
+ """Test creating expert with topics and learnings."""
+ data = {
+ "discovery_description": "Test expert",
+ "full_expertise": "You are an expert.",
+ }
+ topics = [Topic(name="Topic 1"), Topic(name="Topic 2")]
+ learnings = [Learning(name="Learning 1")]
+
+ expert = ExpertDefinition.from_dict(
+ data, Path("/path/to/test_expert"), topics=topics, learnings=learnings
+ )
+
+ assert len(expert.topics) == 2
+ assert len(expert.learnings) == 1
+
+ def test_get_topics_sorted(self) -> None:
+ """Test topics are sorted by most recently updated."""
+ topics = [
+ Topic(name="Old", last_updated=date(2025, 1, 1)),
+ Topic(name="New", last_updated=date(2025, 1, 30)),
+ Topic(name="No Date"),
+ ]
+ expert = ExpertDefinition(
+ name="test",
+ discovery_description="Test",
+ full_expertise="Test",
+ expert_dir=Path("/test"),
+ topics=topics,
+ )
+
+ sorted_topics = expert.get_topics_sorted()
+ assert sorted_topics[0].name == "New"
+ assert sorted_topics[1].name == "Old"
+ assert sorted_topics[2].name == "No Date"
+
+ def test_get_learnings_sorted(self) -> None:
+ """Test learnings are sorted by most recently updated."""
+ learnings = [
+ Learning(name="Old", last_updated=date(2025, 1, 1)),
+ Learning(name="New", last_updated=date(2025, 1, 30)),
+ Learning(name="No Date"),
+ ]
+ expert = ExpertDefinition(
+ name="test",
+ discovery_description="Test",
+ full_expertise="Test",
+ expert_dir=Path("/test"),
+ learnings=learnings,
+ )
+
+ sorted_learnings = expert.get_learnings_sorted()
+ assert sorted_learnings[0].name == "New"
+ assert sorted_learnings[1].name == "Old"
+ assert sorted_learnings[2].name == "No Date"
+
+
+class TestParseTopicFile:
+ """Tests for parsing topic files."""
+
+ def test_parse_valid_topic(self, tmp_path: Path) -> None:
+ """Test parsing a valid topic file."""
+ topic_file = tmp_path / "test_topic.md"
+ topic_file.write_text(
+ """---
+name: Test Topic
+keywords:
+ - testing
+ - unit test
+last_updated: 2025-01-30
+---
+
+This is the topic content.
+"""
+ )
+
+ topic = parse_topic_file(topic_file)
+
+ assert topic.name == "Test Topic"
+ assert topic.keywords == ["testing", "unit test"]
+ assert topic.last_updated == date(2025, 1, 30)
+ assert "topic content" in topic.body
+ assert topic.source_file == topic_file
+
+ def test_parse_minimal_topic(self, tmp_path: Path) -> None:
+ """Test parsing a minimal topic file."""
+ topic_file = tmp_path / "minimal.md"
+ topic_file.write_text(
+ """---
+name: Minimal Topic
+---
+
+Content here.
+"""
+ )
+
+ topic = parse_topic_file(topic_file)
+
+ assert topic.name == "Minimal Topic"
+ assert topic.keywords == []
+ assert topic.last_updated is None
+
+ def test_parse_topic_missing_frontmatter(self, tmp_path: Path) -> None:
+ """Test parsing topic without frontmatter fails."""
+ topic_file = tmp_path / "no_frontmatter.md"
+ topic_file.write_text("Just content without frontmatter")
+
+ with pytest.raises(ExpertParseError, match="frontmatter"):
+ parse_topic_file(topic_file)
+
+ def test_parse_topic_missing_name(self, tmp_path: Path) -> None:
+ """Test parsing topic without name fails."""
+ topic_file = tmp_path / "no_name.md"
+ topic_file.write_text(
+ """---
+keywords:
+ - test
+---
+
+Content
+"""
+ )
+
+ with pytest.raises(ExpertParseError, match="name"):
+ parse_topic_file(topic_file)
+
+ def test_parse_topic_nonexistent(self, tmp_path: Path) -> None:
+ """Test parsing nonexistent topic file fails."""
+ with pytest.raises(ExpertParseError, match="does not exist"):
+ parse_topic_file(tmp_path / "nonexistent.md")
+
+
+class TestParseLearningFile:
+ """Tests for parsing learning files."""
+
+ def test_parse_valid_learning(self, tmp_path: Path) -> None:
+ """Test parsing a valid learning file."""
+ learning_file = tmp_path / "test_learning.md"
+ learning_file.write_text(
+ """---
+name: Test Learning
+last_updated: 2025-01-30
+summarized_result: |
+ Discovered that X causes Y.
+---
+
+## Context
+Full learning content here.
+"""
+ )
+
+ learning = parse_learning_file(learning_file)
+
+ assert learning.name == "Test Learning"
+ assert learning.last_updated == date(2025, 1, 30)
+ assert "Discovered that X causes Y" in learning.summarized_result
+ assert "Full learning content" in learning.body
+
+ def test_parse_minimal_learning(self, tmp_path: Path) -> None:
+ """Test parsing a minimal learning file."""
+ learning_file = tmp_path / "minimal.md"
+ learning_file.write_text(
+ """---
+name: Minimal Learning
+---
+
+Content here.
+"""
+ )
+
+ learning = parse_learning_file(learning_file)
+
+ assert learning.name == "Minimal Learning"
+ assert learning.last_updated is None
+ assert learning.summarized_result is None
+
+ def test_parse_learning_missing_frontmatter(self, tmp_path: Path) -> None:
+ """Test parsing learning without frontmatter fails."""
+ learning_file = tmp_path / "no_frontmatter.md"
+ learning_file.write_text("Just content")
+
+ with pytest.raises(ExpertParseError, match="frontmatter"):
+ parse_learning_file(learning_file)
+
+
+class TestParseExpertDefinition:
+ """Tests for parsing expert definitions."""
+
+ def test_parse_valid_expert(self, tmp_path: Path) -> None:
+ """Test parsing a valid expert definition."""
+ expert_dir = tmp_path / "test_expert"
+ expert_dir.mkdir()
+
+ # Create expert.yml
+ (expert_dir / "expert.yml").write_text(
+ """discovery_description: |
+ Test expert for unit testing
+
+full_expertise: |
+ You are an expert on testing.
+"""
+ )
+
+ # Create topics directory with a topic
+ topics_dir = expert_dir / "topics"
+ topics_dir.mkdir()
+ (topics_dir / "basics.md").write_text(
+ """---
+name: Testing Basics
+keywords:
+ - basics
+last_updated: 2025-01-30
+---
+
+Content
+"""
+ )
+
+ # Create learnings directory with a learning
+ learnings_dir = expert_dir / "learnings"
+ learnings_dir.mkdir()
+ (learnings_dir / "discovery.md").write_text(
+ """---
+name: Important Discovery
+last_updated: 2025-01-20
+summarized_result: Found something important.
+---
+
+Details
+"""
+ )
+
+ expert = parse_expert_definition(expert_dir)
+
+ assert expert.name == "test-expert"
+ assert "unit testing" in expert.discovery_description
+ assert "expert on testing" in expert.full_expertise
+ assert len(expert.topics) == 1
+ assert expert.topics[0].name == "Testing Basics"
+ assert len(expert.learnings) == 1
+ assert expert.learnings[0].name == "Important Discovery"
+
+ def test_parse_expert_no_topics_or_learnings(self, tmp_path: Path) -> None:
+ """Test parsing expert without topics or learnings directories."""
+ expert_dir = tmp_path / "minimal_expert"
+ expert_dir.mkdir()
+
+ (expert_dir / "expert.yml").write_text(
+ """discovery_description: Minimal expert
+full_expertise: Minimal expertise.
+"""
+ )
+
+ expert = parse_expert_definition(expert_dir)
+
+ assert expert.name == "minimal-expert"
+ assert expert.topics == []
+ assert expert.learnings == []
+
+ def test_parse_expert_missing_expert_yml(self, tmp_path: Path) -> None:
+ """Test parsing expert without expert.yml fails."""
+ expert_dir = tmp_path / "no_yml"
+ expert_dir.mkdir()
+
+ with pytest.raises(ExpertParseError, match="expert.yml not found"):
+ parse_expert_definition(expert_dir)
+
+ def test_parse_expert_nonexistent_dir(self, tmp_path: Path) -> None:
+ """Test parsing nonexistent directory fails."""
+ with pytest.raises(ExpertParseError, match="does not exist"):
+ parse_expert_definition(tmp_path / "nonexistent")
+
+ def test_parse_expert_invalid_yml(self, tmp_path: Path) -> None:
+ """Test parsing expert with invalid YAML fails."""
+ expert_dir = tmp_path / "invalid"
+ expert_dir.mkdir()
+ (expert_dir / "expert.yml").write_text("discovery_description: only one field")
+
+ with pytest.raises(ExpertParseError, match="full_expertise"):
+ parse_expert_definition(expert_dir)
+
+
+class TestDiscoverExperts:
+ """Tests for discovering expert directories."""
+
+ def test_discover_experts_multiple(self, tmp_path: Path) -> None:
+ """Test discovering multiple experts."""
+ experts_dir = tmp_path / "experts"
+ experts_dir.mkdir()
+
+ # Create two expert directories
+ for name in ["expert_a", "expert_b"]:
+ expert_dir = experts_dir / name
+ expert_dir.mkdir()
+ (expert_dir / "expert.yml").write_text(
+ f"discovery_description: {name}\nfull_expertise: Content"
+ )
+
+ expert_dirs = discover_experts(experts_dir)
+
+ assert len(expert_dirs) == 2
+ names = {d.name for d in expert_dirs}
+ assert names == {"expert_a", "expert_b"}
+
+ def test_discover_experts_empty(self, tmp_path: Path) -> None:
+ """Test discovering experts in empty directory."""
+ experts_dir = tmp_path / "experts"
+ experts_dir.mkdir()
+
+ expert_dirs = discover_experts(experts_dir)
+
+ assert expert_dirs == []
+
+ def test_discover_experts_nonexistent(self, tmp_path: Path) -> None:
+ """Test discovering experts in nonexistent directory."""
+ expert_dirs = discover_experts(tmp_path / "nonexistent")
+
+ assert expert_dirs == []
+
+ def test_discover_experts_ignores_non_expert_dirs(self, tmp_path: Path) -> None:
+ """Test that directories without expert.yml are ignored."""
+ experts_dir = tmp_path / "experts"
+ experts_dir.mkdir()
+
+ # Valid expert
+ valid_dir = experts_dir / "valid_expert"
+ valid_dir.mkdir()
+ (valid_dir / "expert.yml").write_text(
+ "discovery_description: Valid\nfull_expertise: Content"
+ )
+
+ # Invalid - no expert.yml
+ invalid_dir = experts_dir / "not_an_expert"
+ invalid_dir.mkdir()
+ (invalid_dir / "readme.md").write_text("Not an expert")
+
+ expert_dirs = discover_experts(experts_dir)
+
+ assert len(expert_dirs) == 1
+ assert expert_dirs[0].name == "valid_expert"
+
+
+class TestFormatTopicsMarkdown:
+ """Tests for formatting topics as markdown."""
+
+ def test_format_topics_empty(self) -> None:
+ """Test formatting empty topics list."""
+ expert = ExpertDefinition(
+ name="test",
+ discovery_description="Test",
+ full_expertise="Test",
+ expert_dir=Path("/test"),
+ topics=[],
+ )
+
+ result = format_topics_markdown(expert)
+
+ assert result == "_No topics yet._"
+
+ def test_format_topics_with_content(self) -> None:
+ """Test formatting topics with content."""
+ topics = [
+ Topic(
+ name="First Topic",
+ keywords=["key1", "key2"],
+ source_file=Path("/test/topics/first.md"),
+ ),
+ Topic(name="Second Topic", source_file=Path("/test/topics/second.md")),
+ ]
+ expert = ExpertDefinition(
+ name="test",
+ discovery_description="Test",
+ full_expertise="Test",
+ expert_dir=Path("/test"),
+ topics=topics,
+ )
+
+ result = format_topics_markdown(expert)
+
+ assert "[First Topic](topics/first.md)" in result
+ assert "[Second Topic](topics/second.md)" in result
+ assert "Keywords: key1, key2" in result
+
+
+class TestFormatLearningsMarkdown:
+ """Tests for formatting learnings as markdown."""
+
+ def test_format_learnings_empty(self) -> None:
+ """Test formatting empty learnings list."""
+ expert = ExpertDefinition(
+ name="test",
+ discovery_description="Test",
+ full_expertise="Test",
+ expert_dir=Path("/test"),
+ learnings=[],
+ )
+
+ result = format_learnings_markdown(expert)
+
+ assert result == "_No learnings yet._"
+
+ def test_format_learnings_with_content(self) -> None:
+ """Test formatting learnings with content."""
+ learnings = [
+ Learning(
+ name="First Learning",
+ summarized_result="Found something important.",
+ source_file=Path("/test/learnings/first.md"),
+ ),
+ Learning(name="Second Learning", source_file=Path("/test/learnings/second.md")),
+ ]
+ expert = ExpertDefinition(
+ name="test",
+ discovery_description="Test",
+ full_expertise="Test",
+ expert_dir=Path("/test"),
+ learnings=learnings,
+ )
+
+ result = format_learnings_markdown(expert)
+
+ assert "[First Learning](learnings/first.md)" in result
+ assert "[Second Learning](learnings/second.md)" in result
+ assert "Found something important" in result
diff --git a/tests/unit/test_generator.py b/tests/unit/test_generator.py
deleted file mode 100644
index dd90ba30..00000000
--- a/tests/unit/test_generator.py
+++ /dev/null
@@ -1,547 +0,0 @@
-"""Tests for skill generator."""
-
-from pathlib import Path
-
-import pytest
-
-from deepwork.core.adapters import ClaudeAdapter
-from deepwork.core.generator import GeneratorError, SkillGenerator
-from deepwork.core.parser import Step, parse_job_definition
-
-
-class TestSkillGenerator:
- """Tests for SkillGenerator class."""
-
- def test_init_default_templates_dir(self) -> None:
- """Test initialization with default templates directory."""
- generator = SkillGenerator()
-
- assert generator.templates_dir.exists()
- assert (generator.templates_dir / "claude").exists()
-
- def test_init_custom_templates_dir(self, temp_dir: Path) -> None:
- """Test initialization with custom templates directory."""
- templates_dir = temp_dir / "templates"
- templates_dir.mkdir()
-
- generator = SkillGenerator(templates_dir)
-
- assert generator.templates_dir == templates_dir
-
- def test_init_raises_for_missing_templates_dir(self, temp_dir: Path) -> None:
- """Test initialization raises error for missing templates directory."""
- nonexistent = temp_dir / "nonexistent"
-
- with pytest.raises(GeneratorError, match="Templates directory not found"):
- SkillGenerator(nonexistent)
-
- def test_generate_step_skill_simple_job(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test generating skill for simple job step."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skill_path = generator.generate_step_skill(job, job.steps[0], adapter, temp_dir)
-
- assert skill_path.exists()
- # Step skills use directory/SKILL.md format
- assert skill_path.name == "SKILL.md"
- assert skill_path.parent.name == "simple_job.single_step"
-
- content = skill_path.read_text()
- assert "# simple_job.single_step" in content
- # Single step with no dependencies is treated as standalone
- assert "Standalone skill" in content
- assert "input_param" in content
- assert "output.md" in content
-
- def test_generate_step_skill_complex_job_first_step(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating skill for first step of complex job."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skill_path = generator.generate_step_skill(job, job.steps[0], adapter, temp_dir)
-
- content = skill_path.read_text()
- assert "# competitive_research.identify_competitors" in content
- assert "Step 1/4" in content
- assert "market_segment" in content
- assert "product_category" in content
- # First step has no prerequisites
- assert "## Prerequisites" not in content
- # Has next step
- assert "/competitive_research.primary_research" in content
-
- def test_generate_step_skill_complex_job_middle_step(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating skill for middle step with dependencies."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Generate primary_research (step 2)
- skill_path = generator.generate_step_skill(job, job.steps[1], adapter, temp_dir)
-
- content = skill_path.read_text()
- assert "# competitive_research.primary_research" in content
- assert "Step 2/4" in content
- # Has prerequisites
- assert "## Prerequisites" in content
- assert "/competitive_research.identify_competitors" in content
- # Has file input
- assert "competitors.md" in content
- assert "from `identify_competitors`" in content
- # Has next step
- assert "/competitive_research.secondary_research" in content
-
- def test_generate_step_skill_complex_job_final_step(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating skill for final step."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Generate comparative_report (step 4)
- skill_path = generator.generate_step_skill(job, job.steps[3], adapter, temp_dir)
-
- content = skill_path.read_text()
- assert "# competitive_research.comparative_report" in content
- assert "Step 4/4" in content
- # Has prerequisites
- assert "## Prerequisites" in content
- # Has multiple file inputs
- assert "primary_research.md" in content
- assert "secondary_research.md" in content
- # Final step - no next step
- assert "**Workflow complete**" in content
- assert "## Next Step" not in content
-
- def test_generate_step_skill_raises_for_missing_step(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test that generating skill for non-existent step raises error."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Create a fake step not in the job
-
- fake_step = Step(
- id="fake",
- name="Fake",
- description="Fake",
- instructions_file="steps/fake.md",
- outputs=["fake.md"],
- )
-
- with pytest.raises(GeneratorError, match="Step 'fake' not found"):
- generator.generate_step_skill(job, fake_step, adapter, temp_dir)
-
- def test_generate_step_skill_raises_for_missing_instructions(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test that missing instructions file raises error."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- # Save original instructions file content
- instructions_file = job_dir / "steps" / "single_step.md"
- original_content = instructions_file.read_text()
-
- try:
- # Delete the instructions file
- instructions_file.unlink()
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- with pytest.raises(GeneratorError, match="instructions file not found"):
- generator.generate_step_skill(job, job.steps[0], adapter, temp_dir)
- finally:
- # Restore the file
- instructions_file.write_text(original_content)
-
- def test_generate_all_skills(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test generating skills for all steps in a job (meta + step skills)."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skill_paths = generator.generate_all_skills(job, adapter, temp_dir)
-
- # Now includes meta-skill plus step skills
- assert len(skill_paths) == 5 # 1 meta + 4 steps
- assert all(p.exists() for p in skill_paths)
-
- # Check directory names - meta-skill first, then step skills
- # All files are named SKILL.md inside skill directories
- expected_dirs = [
- "competitive_research", # Meta-skill
- "competitive_research.identify_competitors", # Step skills
- "competitive_research.primary_research",
- "competitive_research.secondary_research",
- "competitive_research.comparative_report",
- ]
- actual_dirs = [p.parent.name for p in skill_paths]
- assert actual_dirs == expected_dirs
- assert all(p.name == "SKILL.md" for p in skill_paths)
-
- def test_generate_meta_skill(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test generating meta-skill for a job."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- meta_skill_path = generator.generate_meta_skill(job, adapter, temp_dir)
-
- assert meta_skill_path.exists()
- assert meta_skill_path.name == "SKILL.md"
- assert meta_skill_path.parent.name == "competitive_research"
-
- content = meta_skill_path.read_text()
- # Check meta-skill content
- assert "# competitive_research" in content
- assert "Available Steps" in content
- assert "identify_competitors" in content
- assert "primary_research" in content
- assert "Skill tool" in content
-
- def test_generate_step_skill_exposed_step(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test generating skill for exposed step."""
- job_dir = fixtures_dir / "jobs" / "exposed_step_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Generate the exposed step (index 1)
- skill_path = generator.generate_step_skill(job, job.steps[1], adapter, temp_dir)
-
- assert skill_path.exists()
- # Uses directory/SKILL.md format whether exposed or not
- assert skill_path.name == "SKILL.md"
- assert skill_path.parent.name == "exposed_job.exposed_step"
-
- def test_generate_all_skills_with_exposed_steps(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating all skills with mix of hidden and exposed steps."""
- job_dir = fixtures_dir / "jobs" / "exposed_step_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skill_paths = generator.generate_all_skills(job, adapter, temp_dir)
-
- # Meta-skill + 2 steps
- assert len(skill_paths) == 3
- assert all(p.exists() for p in skill_paths)
-
- # Check directory names - all use directory/SKILL.md format
- expected_dirs = [
- "exposed_job", # Meta-skill
- "exposed_job.hidden_step", # Step skill
- "exposed_job.exposed_step", # Step skill
- ]
- actual_dirs = [p.parent.name for p in skill_paths]
- assert actual_dirs == expected_dirs
- assert all(p.name == "SKILL.md" for p in skill_paths)
-
-
-class TestConcurrentStepsGeneration:
- """Tests for concurrent steps in skill generation."""
-
- def test_generate_meta_skill_with_concurrent_steps(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating meta-skill for job with concurrent steps."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- meta_skill_path = generator.generate_meta_skill(job, adapter, temp_dir)
-
- assert meta_skill_path.exists()
- content = meta_skill_path.read_text()
-
- # Check meta-skill content has workflow section
- assert "# concurrent_workflow" in content
- assert "full_analysis" in content
-
- # Check concurrent steps are rendered correctly
- assert "Concurrent Steps" in content
- assert "Background Task 1" in content
- assert "Background Task 2" in content
- assert "Background Task 3" in content
- assert "research_web" in content
- assert "research_docs" in content
- assert "research_interviews" in content
-
- def test_meta_skill_context_has_step_entries(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test that meta-skill context includes step_entries with concurrency info."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- context = generator._build_meta_skill_context(job, adapter)
-
- assert "workflows" in context
- assert len(context["workflows"]) == 1
-
- workflow = context["workflows"][0]
- assert "step_entries" in workflow
- assert len(workflow["step_entries"]) == 4
-
- # Check first entry (sequential)
- entry1 = workflow["step_entries"][0]
- assert entry1["is_concurrent"] is False
- assert entry1["step_ids"] == ["setup"]
-
- # Check second entry (concurrent)
- entry2 = workflow["step_entries"][1]
- assert entry2["is_concurrent"] is True
- assert entry2["step_ids"] == ["research_web", "research_docs", "research_interviews"]
- assert "concurrent_steps" in entry2
- assert len(entry2["concurrent_steps"]) == 3
- assert entry2["concurrent_steps"][0]["task_number"] == 1
- assert entry2["concurrent_steps"][0]["id"] == "research_web"
-
- def test_generate_all_skills_with_concurrent_steps(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating all skills for job with concurrent steps."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skill_paths = generator.generate_all_skills(job, adapter, temp_dir)
-
- # 1 meta-skill + 6 step skills
- assert len(skill_paths) == 7
- assert all(p.exists() for p in skill_paths)
-
- # Check all step skills are generated
- expected_dirs = [
- "concurrent_workflow", # Meta-skill
- "concurrent_workflow.setup",
- "concurrent_workflow.research_web",
- "concurrent_workflow.research_docs",
- "concurrent_workflow.research_interviews",
- "concurrent_workflow.compile_results",
- "concurrent_workflow.final_review",
- ]
- actual_dirs = [p.parent.name for p in skill_paths]
- assert actual_dirs == expected_dirs
-
-
-class TestDocSpecIntegration:
- """Tests for doc spec integration in skill generation."""
-
- def test_load_doc_spec_returns_parsed_spec(self, fixtures_dir: Path) -> None:
- """Test that _load_doc_spec loads and parses doc spec files."""
- generator = SkillGenerator()
-
- # Load the valid_report doc spec from fixtures
- doc_spec = generator._load_doc_spec(fixtures_dir, "doc_specs/valid_report.md")
-
- assert doc_spec is not None
- assert doc_spec.name == "Monthly Report"
- assert doc_spec.description == "A monthly summary report"
- assert doc_spec.target_audience == "Team leads"
- assert len(doc_spec.quality_criteria) == 2
- assert doc_spec.quality_criteria[0].name == "Summary"
-
- def test_load_doc_spec_caches_result(self, fixtures_dir: Path) -> None:
- """Test that doc specs are cached after first load."""
- generator = SkillGenerator()
-
- # Load same doc spec twice
- doc_spec1 = generator._load_doc_spec(fixtures_dir, "doc_specs/valid_report.md")
- doc_spec2 = generator._load_doc_spec(fixtures_dir, "doc_specs/valid_report.md")
-
- # Should be the same cached instance
- assert doc_spec1 is doc_spec2
- # Cache should have exactly one entry
- assert len(generator._doc_spec_cache) == 1
-
- def test_load_doc_spec_returns_none_for_missing_file(self, temp_dir: Path) -> None:
- """Test that _load_doc_spec returns None for non-existent file."""
- generator = SkillGenerator()
-
- result = generator._load_doc_spec(temp_dir, "nonexistent.md")
-
- assert result is None
-
- def test_load_doc_spec_returns_none_for_invalid_spec(self, temp_dir: Path) -> None:
- """Test that _load_doc_spec returns None for invalid doc spec file."""
- generator = SkillGenerator()
-
- # Create an invalid doc spec file (missing required fields)
- invalid_spec = temp_dir / "invalid.md"
- invalid_spec.write_text("""---
-name: "Test"
----
-Body content
-""")
-
- result = generator._load_doc_spec(temp_dir, "invalid.md")
-
- assert result is None
-
- def test_generate_step_skill_with_doc_spec(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test generating skill for step with doc spec-referenced output."""
- # Set up the directory structure so the doc spec can be found
- doc_specs_dir = temp_dir / ".deepwork" / "doc_specs"
- doc_specs_dir.mkdir(parents=True)
-
- # Copy the valid_report.md fixture to the expected location
- source_doc_spec = fixtures_dir / "doc_specs" / "valid_report.md"
- target_doc_spec = doc_specs_dir / "valid_report.md"
- target_doc_spec.write_text(source_doc_spec.read_text())
-
- # Parse the job with doc spec
- job_dir = fixtures_dir / "jobs" / "job_with_doc_spec"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Generate skill with project_root set to temp_dir so it finds doc specs
- skill_path = generator.generate_step_skill(
- job, job.steps[0], adapter, temp_dir, project_root=temp_dir
- )
-
- assert skill_path.exists()
- content = skill_path.read_text()
-
- # Verify doc spec info is injected into the skill
- assert "Doc Spec" in content
- assert "Monthly Report" in content
- assert "A monthly summary report" in content
- assert "Target Audience" in content
- assert "Team leads" in content
- assert "Quality Criteria" in content
- assert "Summary" in content
- assert "Must include executive summary" in content
-
- def test_generate_step_skill_without_doc_spec(self, fixtures_dir: Path, temp_dir: Path) -> None:
- """Test generating skill for step without doc spec reference."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- skill_path = generator.generate_step_skill(job, job.steps[0], adapter, temp_dir)
-
- content = skill_path.read_text()
- # Should not have doc spec section
- assert "Doc Spec:" not in content
-
- def test_generate_step_skill_with_missing_doc_spec_file(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test generating skill when doc spec file doesn't exist."""
- # Parse the job with doc spec but don't create the doc spec file
- job_dir = fixtures_dir / "jobs" / "job_with_doc_spec"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Generate skill without the doc spec file present
- # This should work but not include doc spec info
- skill_path = generator.generate_step_skill(
- job, job.steps[0], adapter, temp_dir, project_root=temp_dir
- )
-
- assert skill_path.exists()
- content = skill_path.read_text()
-
- # Should still generate the skill, just without doc spec details
- assert "job_with_doc_spec.generate_report" in content
- # Doc spec section should not appear since file is missing
- assert "Monthly Report" not in content
-
- def test_build_step_context_includes_doc_spec_info(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test that _build_step_context includes doc spec info in outputs."""
- # Set up the directory structure
- doc_specs_dir = temp_dir / ".deepwork" / "doc_specs"
- doc_specs_dir.mkdir(parents=True)
-
- source_doc_spec = fixtures_dir / "doc_specs" / "valid_report.md"
- target_doc_spec = doc_specs_dir / "valid_report.md"
- target_doc_spec.write_text(source_doc_spec.read_text())
-
- job_dir = fixtures_dir / "jobs" / "job_with_doc_spec"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- context = generator._build_step_context(
- job, job.steps[0], 0, adapter, project_root=temp_dir
- )
-
- # Check outputs context has doc spec info
- assert "outputs" in context
- assert len(context["outputs"]) == 1
-
- output_ctx = context["outputs"][0]
- assert output_ctx["file"] == "report.md"
- assert output_ctx["has_doc_spec"] is True
- assert "doc_spec" in output_ctx
-
- doc_spec_ctx = output_ctx["doc_spec"]
- assert doc_spec_ctx["name"] == "Monthly Report"
- assert doc_spec_ctx["description"] == "A monthly summary report"
- assert doc_spec_ctx["target_audience"] == "Team leads"
- assert len(doc_spec_ctx["quality_criteria"]) == 2
- assert doc_spec_ctx["quality_criteria"][0]["name"] == "Summary"
- assert "example_document" in doc_spec_ctx
-
- def test_build_step_context_without_project_root(
- self, fixtures_dir: Path, temp_dir: Path
- ) -> None:
- """Test that _build_step_context handles missing project_root."""
- job_dir = fixtures_dir / "jobs" / "job_with_doc_spec"
- job = parse_job_definition(job_dir)
-
- generator = SkillGenerator()
- adapter = ClaudeAdapter()
-
- # Build context without project_root - should still work but no doc spec
- context = generator._build_step_context(job, job.steps[0], 0, adapter)
-
- output_ctx = context["outputs"][0]
- assert output_ctx["has_doc_spec"] is True # Job still declares it
- # But doc_spec info won't be loaded since no project_root
- assert "doc_spec" not in output_ctx
diff --git a/tests/unit/test_hooks_syncer.py b/tests/unit/test_hooks_syncer.py
index 99edcfdb..7a8cecdb 100644
--- a/tests/unit/test_hooks_syncer.py
+++ b/tests/unit/test_hooks_syncer.py
@@ -5,10 +5,10 @@
from deepwork.core.adapters import ClaudeAdapter
from deepwork.core.hooks_syncer import (
+ ExpertHooks,
HookEntry,
HookSpec,
- JobHooks,
- collect_job_hooks,
+ collect_expert_hooks,
merge_hooks_for_platform,
sync_hooks_to_platform,
)
@@ -19,26 +19,26 @@ class TestHookEntry:
def test_get_command_for_script(self, temp_dir: Path) -> None:
"""Test getting command for a script hook."""
- job_dir = temp_dir / ".deepwork" / "jobs" / "test_job"
- job_dir.mkdir(parents=True)
+ expert_dir = temp_dir / ".deepwork" / "experts" / "test_expert"
+ expert_dir.mkdir(parents=True)
entry = HookEntry(
- job_name="test_job",
- job_dir=job_dir,
+ expert_name="test_expert",
+ expert_dir=expert_dir,
script="test_hook.sh",
)
cmd = entry.get_command(temp_dir)
- assert cmd == ".deepwork/jobs/test_job/hooks/test_hook.sh"
+ assert cmd == ".deepwork/experts/test_expert/hooks/test_hook.sh"
def test_get_command_for_module(self, temp_dir: Path) -> None:
"""Test getting command for a module hook."""
- job_dir = temp_dir / ".deepwork" / "jobs" / "test_job"
- job_dir.mkdir(parents=True)
+ expert_dir = temp_dir / ".deepwork" / "experts" / "test_expert"
+ expert_dir.mkdir(parents=True)
entry = HookEntry(
- job_name="test_job",
- job_dir=job_dir,
+ expert_name="test_expert",
+ expert_dir=expert_dir,
module="deepwork.hooks.rules_check",
)
@@ -46,14 +46,18 @@ def test_get_command_for_module(self, temp_dir: Path) -> None:
assert cmd == "deepwork hook rules_check"
-class TestJobHooks:
- """Tests for JobHooks dataclass."""
+class TestExpertHooks:
+ """Tests for ExpertHooks dataclass."""
- def test_from_job_dir_with_hooks(self, temp_dir: Path) -> None:
- """Test loading hooks from job directory."""
- job_dir = temp_dir / "test_job"
- hooks_dir = job_dir / "hooks"
+ def test_from_expert_dir_with_hooks(self, temp_dir: Path) -> None:
+ """Test loading hooks from expert directory."""
+ expert_dir = temp_dir / "test_expert"
+ hooks_dir = expert_dir / "hooks"
hooks_dir.mkdir(parents=True)
+ # Create expert.yml so it's recognized as a valid expert
+ (expert_dir / "expert.yml").write_text(
+ "discovery_description: test\nfull_expertise: test\n"
+ )
# Create global_hooks.yml
hooks_file = hooks_dir / "global_hooks.yml"
@@ -67,21 +71,25 @@ def test_from_job_dir_with_hooks(self, temp_dir: Path) -> None:
"""
)
- result = JobHooks.from_job_dir(job_dir)
+ result = ExpertHooks.from_expert_dir(expert_dir)
assert result is not None
- assert result.job_name == "test_job"
+ assert result.expert_name == "test_expert"
assert len(result.hooks["UserPromptSubmit"]) == 1
assert result.hooks["UserPromptSubmit"][0].script == "capture.sh"
assert len(result.hooks["Stop"]) == 2
assert result.hooks["Stop"][0].script == "rules_check.sh"
assert result.hooks["Stop"][1].script == "cleanup.sh"
- def test_from_job_dir_with_module_hooks(self, temp_dir: Path) -> None:
- """Test loading module-based hooks from job directory."""
- job_dir = temp_dir / "test_job"
- hooks_dir = job_dir / "hooks"
+ def test_from_expert_dir_with_module_hooks(self, temp_dir: Path) -> None:
+ """Test loading module-based hooks from expert directory."""
+ expert_dir = temp_dir / "test_expert"
+ hooks_dir = expert_dir / "hooks"
hooks_dir.mkdir(parents=True)
+ # Create expert.yml so it's recognized as a valid expert
+ (expert_dir / "expert.yml").write_text(
+ "discovery_description: test\nfull_expertise: test\n"
+ )
# Create global_hooks.yml with module format
hooks_file = hooks_dir / "global_hooks.yml"
@@ -94,103 +102,112 @@ def test_from_job_dir_with_module_hooks(self, temp_dir: Path) -> None:
"""
)
- result = JobHooks.from_job_dir(job_dir)
+ result = ExpertHooks.from_expert_dir(expert_dir)
assert result is not None
assert result.hooks["UserPromptSubmit"][0].script == "capture.sh"
assert result.hooks["Stop"][0].module == "deepwork.hooks.rules_check"
assert result.hooks["Stop"][0].script is None
- def test_from_job_dir_no_hooks_file(self, temp_dir: Path) -> None:
+ def test_from_expert_dir_no_hooks_file(self, temp_dir: Path) -> None:
"""Test returns None when no hooks file exists."""
- job_dir = temp_dir / "test_job"
- job_dir.mkdir(parents=True)
+ expert_dir = temp_dir / "test_expert"
+ expert_dir.mkdir(parents=True)
- result = JobHooks.from_job_dir(job_dir)
+ result = ExpertHooks.from_expert_dir(expert_dir)
assert result is None
- def test_from_job_dir_empty_hooks_file(self, temp_dir: Path) -> None:
+ def test_from_expert_dir_empty_hooks_file(self, temp_dir: Path) -> None:
"""Test returns None when hooks file is empty."""
- job_dir = temp_dir / "test_job"
- hooks_dir = job_dir / "hooks"
+ expert_dir = temp_dir / "test_expert"
+ hooks_dir = expert_dir / "hooks"
hooks_dir.mkdir(parents=True)
hooks_file = hooks_dir / "global_hooks.yml"
hooks_file.write_text("")
- result = JobHooks.from_job_dir(job_dir)
+ result = ExpertHooks.from_expert_dir(expert_dir)
assert result is None
- def test_from_job_dir_single_script_as_string(self, temp_dir: Path) -> None:
+ def test_from_expert_dir_single_script_as_string(self, temp_dir: Path) -> None:
"""Test parsing single script as string instead of list."""
- job_dir = temp_dir / "test_job"
- hooks_dir = job_dir / "hooks"
+ expert_dir = temp_dir / "test_expert"
+ hooks_dir = expert_dir / "hooks"
hooks_dir.mkdir(parents=True)
hooks_file = hooks_dir / "global_hooks.yml"
hooks_file.write_text("Stop: cleanup.sh\n")
- result = JobHooks.from_job_dir(job_dir)
+ result = ExpertHooks.from_expert_dir(expert_dir)
assert result is not None
assert len(result.hooks["Stop"]) == 1
assert result.hooks["Stop"][0].script == "cleanup.sh"
-class TestCollectJobHooks:
- """Tests for collect_job_hooks function."""
+class TestCollectExpertHooks:
+ """Tests for collect_expert_hooks function."""
- def test_collects_hooks_from_multiple_jobs(self, temp_dir: Path) -> None:
- """Test collecting hooks from multiple job directories."""
- jobs_dir = temp_dir / "jobs"
+ def test_collects_hooks_from_multiple_experts(self, temp_dir: Path) -> None:
+ """Test collecting hooks from multiple expert directories."""
+ experts_dir = temp_dir / "experts"
- # Create first job with hooks
- job1_dir = jobs_dir / "job1"
- (job1_dir / "hooks").mkdir(parents=True)
- (job1_dir / "hooks" / "global_hooks.yml").write_text("Stop:\n - hook1.sh\n")
+ # Create first expert with hooks
+ expert1_dir = experts_dir / "expert1"
+ (expert1_dir / "hooks").mkdir(parents=True)
+ (expert1_dir / "expert.yml").write_text(
+ "discovery_description: test\nfull_expertise: test\n"
+ )
+ (expert1_dir / "hooks" / "global_hooks.yml").write_text("Stop:\n - hook1.sh\n")
- # Create second job with hooks
- job2_dir = jobs_dir / "job2"
- (job2_dir / "hooks").mkdir(parents=True)
- (job2_dir / "hooks" / "global_hooks.yml").write_text("Stop:\n - hook2.sh\n")
+ # Create second expert with hooks
+ expert2_dir = experts_dir / "expert2"
+ (expert2_dir / "hooks").mkdir(parents=True)
+ (expert2_dir / "expert.yml").write_text(
+ "discovery_description: test\nfull_expertise: test\n"
+ )
+ (expert2_dir / "hooks" / "global_hooks.yml").write_text("Stop:\n - hook2.sh\n")
- # Create job without hooks
- job3_dir = jobs_dir / "job3"
- job3_dir.mkdir(parents=True)
+ # Create expert without hooks
+ expert3_dir = experts_dir / "expert3"
+ expert3_dir.mkdir(parents=True)
+ (expert3_dir / "expert.yml").write_text(
+ "discovery_description: test\nfull_expertise: test\n"
+ )
- result = collect_job_hooks(jobs_dir)
+ result = collect_expert_hooks(experts_dir)
assert len(result) == 2
- job_names = {jh.job_name for jh in result}
- assert job_names == {"job1", "job2"}
+ expert_names = {eh.expert_name for eh in result}
+ assert expert_names == {"expert1", "expert2"}
def test_returns_empty_for_nonexistent_dir(self, temp_dir: Path) -> None:
- """Test returns empty list when jobs dir doesn't exist."""
- jobs_dir = temp_dir / "nonexistent"
- result = collect_job_hooks(jobs_dir)
+ """Test returns empty list when experts dir doesn't exist."""
+ experts_dir = temp_dir / "nonexistent"
+ result = collect_expert_hooks(experts_dir)
assert result == []
class TestMergeHooksForPlatform:
"""Tests for merge_hooks_for_platform function."""
- def test_merges_hooks_from_multiple_jobs(self, temp_dir: Path) -> None:
- """Test merging hooks from multiple jobs."""
- # Create job directories
- job1_dir = temp_dir / ".deepwork" / "jobs" / "job1"
- job2_dir = temp_dir / ".deepwork" / "jobs" / "job2"
- job1_dir.mkdir(parents=True)
- job2_dir.mkdir(parents=True)
-
- job_hooks_list = [
- JobHooks(
- job_name="job1",
- job_dir=job1_dir,
+ def test_merges_hooks_from_multiple_experts(self, temp_dir: Path) -> None:
+ """Test merging hooks from multiple experts."""
+ # Create expert directories
+ expert1_dir = temp_dir / ".deepwork" / "experts" / "expert1"
+ expert2_dir = temp_dir / ".deepwork" / "experts" / "expert2"
+ expert1_dir.mkdir(parents=True)
+ expert2_dir.mkdir(parents=True)
+
+ expert_hooks_list = [
+ ExpertHooks(
+ expert_name="expert1",
+ expert_dir=expert1_dir,
hooks={"Stop": [HookSpec(script="hook1.sh")]},
),
- JobHooks(
- job_name="job2",
- job_dir=job2_dir,
+ ExpertHooks(
+ expert_name="expert2",
+ expert_dir=expert2_dir,
hooks={
"Stop": [HookSpec(script="hook2.sh")],
"UserPromptSubmit": [HookSpec(script="capture.sh")],
@@ -198,7 +215,7 @@ def test_merges_hooks_from_multiple_jobs(self, temp_dir: Path) -> None:
),
]
- result = merge_hooks_for_platform(job_hooks_list, temp_dir)
+ result = merge_hooks_for_platform(expert_hooks_list, temp_dir)
assert "Stop" in result
assert "UserPromptSubmit" in result
@@ -207,19 +224,19 @@ def test_merges_hooks_from_multiple_jobs(self, temp_dir: Path) -> None:
def test_avoids_duplicate_hooks(self, temp_dir: Path) -> None:
"""Test that duplicate hooks are not added."""
- job_dir = temp_dir / ".deepwork" / "jobs" / "job1"
- job_dir.mkdir(parents=True)
-
- # Same hook in same job (shouldn't happen but test anyway)
- job_hooks_list = [
- JobHooks(
- job_name="job1",
- job_dir=job_dir,
+ expert_dir = temp_dir / ".deepwork" / "experts" / "expert1"
+ expert_dir.mkdir(parents=True)
+
+ # Same hook in same expert (shouldn't happen but test anyway)
+ expert_hooks_list = [
+ ExpertHooks(
+ expert_name="expert1",
+ expert_dir=expert_dir,
hooks={"Stop": [HookSpec(script="hook.sh"), HookSpec(script="hook.sh")]},
),
]
- result = merge_hooks_for_platform(job_hooks_list, temp_dir)
+ result = merge_hooks_for_platform(expert_hooks_list, temp_dir)
# Should only have one entry
assert len(result["Stop"]) == 1
@@ -231,18 +248,18 @@ def test_duplicates_stop_hooks_to_subagent_stop(self, temp_dir: Path) -> None:
is defined, it should also be registered for SubagentStop so the hook
triggers for both the main agent and subagents.
"""
- job_dir = temp_dir / ".deepwork" / "jobs" / "job1"
- job_dir.mkdir(parents=True)
+ expert_dir = temp_dir / ".deepwork" / "experts" / "expert1"
+ expert_dir.mkdir(parents=True)
- job_hooks_list = [
- JobHooks(
- job_name="job1",
- job_dir=job_dir,
+ expert_hooks_list = [
+ ExpertHooks(
+ expert_name="expert1",
+ expert_dir=expert_dir,
hooks={"Stop": [HookSpec(script="hook.sh")]},
),
]
- result = merge_hooks_for_platform(job_hooks_list, temp_dir)
+ result = merge_hooks_for_platform(expert_hooks_list, temp_dir)
# Should have both Stop and SubagentStop events
assert "Stop" in result
@@ -253,22 +270,22 @@ def test_duplicates_stop_hooks_to_subagent_stop(self, temp_dir: Path) -> None:
# Both should have the same hook command
stop_cmd = result["Stop"][0]["hooks"][0]["command"]
subagent_stop_cmd = result["SubagentStop"][0]["hooks"][0]["command"]
- assert stop_cmd == subagent_stop_cmd == ".deepwork/jobs/job1/hooks/hook.sh"
+ assert stop_cmd == subagent_stop_cmd == ".deepwork/experts/expert1/hooks/hook.sh"
def test_does_not_duplicate_subagent_stop_if_no_stop(self, temp_dir: Path) -> None:
"""Test that SubagentStop is not created if there are no Stop hooks."""
- job_dir = temp_dir / ".deepwork" / "jobs" / "job1"
- job_dir.mkdir(parents=True)
+ expert_dir = temp_dir / ".deepwork" / "experts" / "expert1"
+ expert_dir.mkdir(parents=True)
- job_hooks_list = [
- JobHooks(
- job_name="job1",
- job_dir=job_dir,
+ expert_hooks_list = [
+ ExpertHooks(
+ expert_name="expert1",
+ expert_dir=expert_dir,
hooks={"UserPromptSubmit": [HookSpec(script="capture.sh")]},
),
]
- result = merge_hooks_for_platform(job_hooks_list, temp_dir)
+ result = merge_hooks_for_platform(expert_hooks_list, temp_dir)
# Should only have UserPromptSubmit, not SubagentStop
assert "UserPromptSubmit" in result
@@ -286,19 +303,19 @@ def test_syncs_hooks_via_adapter(self, temp_dir: Path) -> None:
adapter = ClaudeAdapter(temp_dir)
- # Create job directories
- job_dir = temp_dir / ".deepwork" / "jobs" / "test_job"
- job_dir.mkdir(parents=True)
+ # Create expert directories
+ expert_dir = temp_dir / ".deepwork" / "experts" / "test_expert"
+ expert_dir.mkdir(parents=True)
- job_hooks_list = [
- JobHooks(
- job_name="test_job",
- job_dir=job_dir,
+ expert_hooks_list = [
+ ExpertHooks(
+ expert_name="test_expert",
+ expert_dir=expert_dir,
hooks={"Stop": [HookSpec(script="test_hook.sh")]},
),
]
- count = sync_hooks_to_platform(temp_dir, adapter, job_hooks_list)
+ count = sync_hooks_to_platform(temp_dir, adapter, expert_hooks_list)
# Count is 2 because Stop hooks are also registered for SubagentStop
assert count == 2
@@ -342,18 +359,18 @@ def test_merges_with_existing_settings(self, temp_dir: Path) -> None:
adapter = ClaudeAdapter(temp_dir)
- job_dir = temp_dir / ".deepwork" / "jobs" / "test_job"
- job_dir.mkdir(parents=True)
+ expert_dir = temp_dir / ".deepwork" / "experts" / "test_expert"
+ expert_dir.mkdir(parents=True)
- job_hooks_list = [
- JobHooks(
- job_name="test_job",
- job_dir=job_dir,
+ expert_hooks_list = [
+ ExpertHooks(
+ expert_name="test_expert",
+ expert_dir=expert_dir,
hooks={"Stop": [HookSpec(script="new_hook.sh")]},
),
]
- sync_hooks_to_platform(temp_dir, adapter, job_hooks_list)
+ sync_hooks_to_platform(temp_dir, adapter, expert_hooks_list)
with open(settings_file) as f:
settings = json.load(f)
diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py
deleted file mode 100644
index 0c968242..00000000
--- a/tests/unit/test_parser.py
+++ /dev/null
@@ -1,565 +0,0 @@
-"""Tests for job definition parser."""
-
-from pathlib import Path
-
-import pytest
-
-from deepwork.core.parser import (
- JobDefinition,
- OutputSpec,
- ParseError,
- Step,
- StepInput,
- parse_job_definition,
-)
-
-
-class TestStepInput:
- """Tests for StepInput dataclass."""
-
- def test_user_input(self) -> None:
- """Test user parameter input."""
- inp = StepInput(name="param1", description="First parameter")
-
- assert inp.is_user_input()
- assert not inp.is_file_input()
-
- def test_file_input(self) -> None:
- """Test file input from previous step."""
- inp = StepInput(file="data.md", from_step="step1")
-
- assert inp.is_file_input()
- assert not inp.is_user_input()
-
- def test_from_dict_user_input(self) -> None:
- """Test creating user input from dictionary."""
- data = {"name": "param1", "description": "First parameter"}
- inp = StepInput.from_dict(data)
-
- assert inp.name == "param1"
- assert inp.description == "First parameter"
- assert inp.is_user_input()
-
- def test_from_dict_file_input(self) -> None:
- """Test creating file input from dictionary."""
- data = {"file": "data.md", "from_step": "step1"}
- inp = StepInput.from_dict(data)
-
- assert inp.file == "data.md"
- assert inp.from_step == "step1"
- assert inp.is_file_input()
-
-
-class TestOutputSpec:
- """Tests for OutputSpec dataclass."""
-
- def test_simple_output(self) -> None:
- """Test simple output without doc spec."""
- output = OutputSpec(file="output.md")
-
- assert output.file == "output.md"
- assert output.doc_spec is None
- assert not output.has_doc_spec()
-
- def test_output_with_doc_spec(self) -> None:
- """Test output with doc spec reference."""
- output = OutputSpec(file="report.md", doc_spec=".deepwork/doc_specs/monthly_report.md")
-
- assert output.file == "report.md"
- assert output.doc_spec == ".deepwork/doc_specs/monthly_report.md"
- assert output.has_doc_spec()
-
- def test_from_dict_string(self) -> None:
- """Test creating output from string."""
- output = OutputSpec.from_dict("output.md")
-
- assert output.file == "output.md"
- assert output.doc_spec is None
- assert not output.has_doc_spec()
-
- def test_from_dict_simple_object(self) -> None:
- """Test creating output from dict without doc spec."""
- data = {"file": "output.md"}
- output = OutputSpec.from_dict(data)
-
- assert output.file == "output.md"
- assert output.doc_spec is None
- assert not output.has_doc_spec()
-
- def test_from_dict_with_doc_spec(self) -> None:
- """Test creating output from dict with doc spec."""
- data = {"file": "report.md", "doc_spec": ".deepwork/doc_specs/monthly_report.md"}
- output = OutputSpec.from_dict(data)
-
- assert output.file == "report.md"
- assert output.doc_spec == ".deepwork/doc_specs/monthly_report.md"
- assert output.has_doc_spec()
-
-
-class TestStep:
- """Tests for Step dataclass."""
-
- def test_from_dict_minimal(self) -> None:
- """Test creating step from minimal dictionary."""
- data = {
- "id": "step1",
- "name": "Step 1",
- "description": "First step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- }
- step = Step.from_dict(data)
-
- assert step.id == "step1"
- assert step.name == "Step 1"
- assert step.description == "First step"
- assert step.instructions_file == "steps/step1.md"
- assert len(step.outputs) == 1
- assert step.outputs[0].file == "output.md"
- assert not step.outputs[0].has_doc_spec()
- assert step.inputs == []
- assert step.dependencies == []
-
- def test_from_dict_with_doc_spec_output(self) -> None:
- """Test creating step with doc spec-referenced output."""
- data = {
- "id": "step1",
- "name": "Step 1",
- "description": "First step",
- "instructions_file": "steps/step1.md",
- "outputs": [
- "simple_output.md",
- {"file": "report.md", "doc_spec": ".deepwork/doc_specs/monthly_report.md"},
- ],
- }
- step = Step.from_dict(data)
-
- assert len(step.outputs) == 2
- assert step.outputs[0].file == "simple_output.md"
- assert not step.outputs[0].has_doc_spec()
- assert step.outputs[1].file == "report.md"
- assert step.outputs[1].doc_spec == ".deepwork/doc_specs/monthly_report.md"
- assert step.outputs[1].has_doc_spec()
-
- def test_from_dict_with_inputs(self) -> None:
- """Test creating step with inputs."""
- data = {
- "id": "step1",
- "name": "Step 1",
- "description": "First step",
- "instructions_file": "steps/step1.md",
- "inputs": [
- {"name": "param1", "description": "Parameter 1"},
- {"file": "data.md", "from_step": "step0"},
- ],
- "outputs": ["output.md"],
- "dependencies": ["step0"],
- }
- step = Step.from_dict(data)
-
- assert len(step.inputs) == 2
- assert step.inputs[0].is_user_input()
- assert step.inputs[1].is_file_input()
- assert step.dependencies == ["step0"]
-
- def test_from_dict_exposed_default_false(self) -> None:
- """Test that exposed defaults to False."""
- data = {
- "id": "step1",
- "name": "Step 1",
- "description": "First step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- }
- step = Step.from_dict(data)
-
- assert step.exposed is False
-
- def test_from_dict_exposed_true(self) -> None:
- """Test creating step with exposed=True."""
- data = {
- "id": "step1",
- "name": "Step 1",
- "description": "First step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "exposed": True,
- }
- step = Step.from_dict(data)
-
- assert step.exposed is True
-
-
-class TestJobDefinition:
- """Tests for JobDefinition dataclass."""
-
- def test_get_step(self, fixtures_dir: Path) -> None:
- """Test getting step by ID."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- step = job.get_step("single_step")
- assert step is not None
- assert step.id == "single_step"
-
- assert job.get_step("nonexistent") is None
-
- def test_validate_dependencies_valid(self, fixtures_dir: Path) -> None:
- """Test validation passes for valid dependencies."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- # Should not raise
- job.validate_dependencies()
-
- def test_validate_dependencies_missing_step(self) -> None:
- """Test validation fails for missing dependency."""
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=["output.md"],
- dependencies=["nonexistent"],
- )
- ],
- job_dir=Path("/tmp"),
- )
-
- with pytest.raises(ParseError, match="depends on non-existent step"):
- job.validate_dependencies()
-
- def test_validate_dependencies_circular(self) -> None:
- """Test validation fails for circular dependencies."""
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=["output.md"],
- dependencies=["step2"],
- ),
- Step(
- id="step2",
- name="Step 2",
- description="Step",
- instructions_file="steps/step2.md",
- outputs=["output.md"],
- dependencies=["step1"],
- ),
- ],
- job_dir=Path("/tmp"),
- )
-
- with pytest.raises(ParseError, match="Circular dependency detected"):
- job.validate_dependencies()
-
- def test_validate_file_inputs_valid(self, fixtures_dir: Path) -> None:
- """Test file input validation passes for valid inputs."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- # Should not raise
- job.validate_file_inputs()
-
- def test_validate_file_inputs_missing_step(self) -> None:
- """Test file input validation fails for missing from_step."""
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- inputs=[StepInput(file="data.md", from_step="nonexistent")],
- outputs=["output.md"],
- dependencies=["nonexistent"],
- )
- ],
- job_dir=Path("/tmp"),
- )
-
- with pytest.raises(ParseError, match="references non-existent step"):
- job.validate_file_inputs()
-
- def test_validate_file_inputs_not_in_dependencies(self) -> None:
- """Test file input validation fails if from_step not in dependencies."""
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=["output.md"],
- ),
- Step(
- id="step2",
- name="Step 2",
- description="Step",
- instructions_file="steps/step2.md",
- inputs=[StepInput(file="data.md", from_step="step1")],
- outputs=["output.md"],
- # Missing step1 in dependencies!
- dependencies=[],
- ),
- ],
- job_dir=Path("/tmp"),
- )
-
- with pytest.raises(ParseError, match="not in dependencies"):
- job.validate_file_inputs()
-
-
-class TestParseJobDefinition:
- """Tests for parse_job_definition function."""
-
- def test_parses_simple_job(self, fixtures_dir: Path) -> None:
- """Test parsing simple job definition."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- assert job.name == "simple_job"
- assert job.summary == "A simple single-step job for testing"
- assert "DeepWork framework" in job.description # Multi-line description
- assert len(job.steps) == 1
- assert job.steps[0].id == "single_step"
- assert job.job_dir == job_dir
-
- def test_parses_complex_job(self, fixtures_dir: Path) -> None:
- """Test parsing complex job with dependencies."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- assert job.name == "competitive_research"
- assert len(job.steps) == 4
- assert job.steps[0].id == "identify_competitors"
- assert job.steps[1].id == "primary_research"
- assert job.steps[2].id == "secondary_research"
- assert job.steps[3].id == "comparative_report"
-
- # Check dependencies
- assert job.steps[0].dependencies == []
- assert job.steps[1].dependencies == ["identify_competitors"]
- assert "identify_competitors" in job.steps[2].dependencies
- assert "primary_research" in job.steps[2].dependencies
- assert "primary_research" in job.steps[3].dependencies
- assert "secondary_research" in job.steps[3].dependencies
-
- def test_parses_user_inputs(self, fixtures_dir: Path) -> None:
- """Test parsing step with user inputs."""
- job_dir = fixtures_dir / "jobs" / "simple_job"
- job = parse_job_definition(job_dir)
-
- step = job.steps[0]
- assert len(step.inputs) == 1
- assert step.inputs[0].is_user_input()
- assert step.inputs[0].name == "input_param"
-
- def test_parses_file_inputs(self, fixtures_dir: Path) -> None:
- """Test parsing step with file inputs."""
- job_dir = fixtures_dir / "jobs" / "complex_job"
- job = parse_job_definition(job_dir)
-
- step = job.steps[1] # primary_research
- assert len(step.inputs) == 1
- assert step.inputs[0].is_file_input()
- assert step.inputs[0].file == "competitors.md"
- assert step.inputs[0].from_step == "identify_competitors"
-
- def test_parses_exposed_steps(self, fixtures_dir: Path) -> None:
- """Test parsing job with exposed and hidden steps."""
- job_dir = fixtures_dir / "jobs" / "exposed_step_job"
- job = parse_job_definition(job_dir)
-
- assert len(job.steps) == 2
- # First step is hidden by default
- assert job.steps[0].id == "hidden_step"
- assert job.steps[0].exposed is False
- # Second step is explicitly exposed
- assert job.steps[1].id == "exposed_step"
- assert job.steps[1].exposed is True
-
- def test_raises_for_missing_directory(self, temp_dir: Path) -> None:
- """Test parsing fails for missing directory."""
- nonexistent = temp_dir / "nonexistent"
-
- with pytest.raises(ParseError, match="does not exist"):
- parse_job_definition(nonexistent)
-
- def test_raises_for_file_instead_of_directory(self, temp_dir: Path) -> None:
- """Test parsing fails for file path."""
- file_path = temp_dir / "file.txt"
- file_path.write_text("content")
-
- with pytest.raises(ParseError, match="not a directory"):
- parse_job_definition(file_path)
-
- def test_raises_for_missing_job_yml(self, temp_dir: Path) -> None:
- """Test parsing fails for directory without job.yml."""
- job_dir = temp_dir / "job"
- job_dir.mkdir()
-
- with pytest.raises(ParseError, match="job.yml not found"):
- parse_job_definition(job_dir)
-
- def test_raises_for_empty_job_yml(self, temp_dir: Path) -> None:
- """Test parsing fails for empty job.yml."""
- job_dir = temp_dir / "job"
- job_dir.mkdir()
- (job_dir / "job.yml").write_text("")
-
- with pytest.raises(ParseError, match="validation failed"):
- parse_job_definition(job_dir)
-
- def test_raises_for_invalid_yaml(self, temp_dir: Path) -> None:
- """Test parsing fails for invalid YAML."""
- job_dir = temp_dir / "job"
- job_dir.mkdir()
- (job_dir / "job.yml").write_text("invalid: [yaml: content")
-
- with pytest.raises(ParseError, match="Failed to load"):
- parse_job_definition(job_dir)
-
- def test_raises_for_invalid_schema(self, fixtures_dir: Path) -> None:
- """Test parsing fails for schema validation errors."""
- job_dir = fixtures_dir / "jobs" / "invalid_job"
-
- with pytest.raises(ParseError, match="validation failed"):
- parse_job_definition(job_dir)
-
-
-class TestConcurrentSteps:
- """Tests for concurrent step parsing in workflows."""
-
- def test_parses_concurrent_steps_workflow(self, fixtures_dir: Path) -> None:
- """Test parsing job with concurrent steps in workflow."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- assert job.name == "concurrent_workflow"
- assert len(job.workflows) == 1
- assert job.workflows[0].name == "full_analysis"
-
- def test_workflow_step_entries(self, fixtures_dir: Path) -> None:
- """Test workflow step_entries structure with concurrent steps."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- workflow = job.workflows[0]
- assert len(workflow.step_entries) == 4
-
- # First entry: sequential step
- assert not workflow.step_entries[0].is_concurrent
- assert workflow.step_entries[0].step_ids == ["setup"]
-
- # Second entry: concurrent steps
- assert workflow.step_entries[1].is_concurrent
- assert workflow.step_entries[1].step_ids == [
- "research_web",
- "research_docs",
- "research_interviews",
- ]
-
- # Third entry: sequential step
- assert not workflow.step_entries[2].is_concurrent
- assert workflow.step_entries[2].step_ids == ["compile_results"]
-
- # Fourth entry: sequential step
- assert not workflow.step_entries[3].is_concurrent
- assert workflow.step_entries[3].step_ids == ["final_review"]
-
- def test_workflow_flattened_steps(self, fixtures_dir: Path) -> None:
- """Test backward-compatible flattened steps list."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- workflow = job.workflows[0]
- # Flattened list should include all step IDs
- assert workflow.steps == [
- "setup",
- "research_web",
- "research_docs",
- "research_interviews",
- "compile_results",
- "final_review",
- ]
-
- def test_get_step_entry_for_step(self, fixtures_dir: Path) -> None:
- """Test getting the step entry containing a step."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- workflow = job.workflows[0]
-
- # Sequential step
- entry = workflow.get_step_entry_for_step("setup")
- assert entry is not None
- assert not entry.is_concurrent
- assert entry.step_ids == ["setup"]
-
- # Concurrent step
- entry = workflow.get_step_entry_for_step("research_web")
- assert entry is not None
- assert entry.is_concurrent
- assert "research_web" in entry.step_ids
-
- def test_get_step_entry_position_in_workflow(self, fixtures_dir: Path) -> None:
- """Test getting entry-based position in workflow."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- # Sequential step
- result = job.get_step_entry_position_in_workflow("setup")
- assert result is not None
- entry_pos, total_entries, entry = result
- assert entry_pos == 1
- assert total_entries == 4
- assert not entry.is_concurrent
-
- # Concurrent step - all share same entry position
- for step_id in ["research_web", "research_docs", "research_interviews"]:
- result = job.get_step_entry_position_in_workflow(step_id)
- assert result is not None
- entry_pos, total_entries, entry = result
- assert entry_pos == 2 # All in second position
- assert total_entries == 4
- assert entry.is_concurrent
-
- def test_get_concurrent_step_info(self, fixtures_dir: Path) -> None:
- """Test getting info about position within concurrent group."""
- job_dir = fixtures_dir / "jobs" / "concurrent_steps_job"
- job = parse_job_definition(job_dir)
-
- # Sequential step returns None
- assert job.get_concurrent_step_info("setup") is None
-
- # Concurrent steps return their position in group
- result = job.get_concurrent_step_info("research_web")
- assert result == (1, 3)
-
- result = job.get_concurrent_step_info("research_docs")
- assert result == (2, 3)
-
- result = job.get_concurrent_step_info("research_interviews")
- assert result == (3, 3)
diff --git a/tests/unit/test_stop_hooks.py b/tests/unit/test_stop_hooks.py
deleted file mode 100644
index 96cdeb5b..00000000
--- a/tests/unit/test_stop_hooks.py
+++ /dev/null
@@ -1,860 +0,0 @@
-"""Tests for stop hook functionality."""
-
-from pathlib import Path
-
-import pytest
-
-from deepwork.core.adapters import ClaudeAdapter
-from deepwork.core.generator import GeneratorError, SkillGenerator
-from deepwork.core.parser import HookAction, JobDefinition, OutputSpec, Step, StopHook
-from deepwork.schemas.job_schema import JOB_SCHEMA
-from deepwork.utils.validation import ValidationError, validate_against_schema
-
-
-class TestStopHook:
- """Tests for StopHook dataclass."""
-
- def test_is_prompt(self) -> None:
- """Test is_prompt returns True for prompt hooks."""
- hook = StopHook(prompt="Check quality")
- assert hook.is_prompt() is True
- assert hook.is_prompt_file() is False
- assert hook.is_script() is False
-
- def test_is_prompt_file(self) -> None:
- """Test is_prompt_file returns True for prompt file hooks."""
- hook = StopHook(prompt_file="hooks/check.md")
- assert hook.is_prompt() is False
- assert hook.is_prompt_file() is True
- assert hook.is_script() is False
-
- def test_is_script(self) -> None:
- """Test is_script returns True for script hooks."""
- hook = StopHook(script="hooks/validate.sh")
- assert hook.is_prompt() is False
- assert hook.is_prompt_file() is False
- assert hook.is_script() is True
-
- def test_from_dict_prompt(self) -> None:
- """Test from_dict creates prompt hook."""
- data = {"prompt": "Verify all criteria are met"}
- hook = StopHook.from_dict(data)
- assert hook.prompt == "Verify all criteria are met"
- assert hook.prompt_file is None
- assert hook.script is None
-
- def test_from_dict_prompt_file(self) -> None:
- """Test from_dict creates prompt file hook."""
- data = {"prompt_file": "hooks/quality.md"}
- hook = StopHook.from_dict(data)
- assert hook.prompt is None
- assert hook.prompt_file == "hooks/quality.md"
- assert hook.script is None
-
- def test_from_dict_script(self) -> None:
- """Test from_dict creates script hook."""
- data = {"script": "hooks/validate.sh"}
- hook = StopHook.from_dict(data)
- assert hook.prompt is None
- assert hook.prompt_file is None
- assert hook.script == "hooks/validate.sh"
-
-
-class TestStepWithStopHooks:
- """Tests for Step with stop_hooks."""
-
- def test_step_with_no_stop_hooks(self) -> None:
- """Test step without stop hooks."""
- step = Step(
- id="test",
- name="Test Step",
- description="A test step",
- instructions_file="steps/test.md",
- outputs=[OutputSpec(file="output.md")],
- )
- assert step.stop_hooks == []
-
- def test_step_with_single_stop_hook(self) -> None:
- """Test step with single stop hook (using hooks dict)."""
- step = Step(
- id="test",
- name="Test Step",
- description="A test step",
- instructions_file="steps/test.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={"after_agent": [HookAction(prompt="Check quality")]},
- )
- assert len(step.stop_hooks) == 1
- assert step.stop_hooks[0].is_prompt()
- assert step.stop_hooks[0].prompt == "Check quality"
-
- def test_step_with_multiple_stop_hooks(self) -> None:
- """Test step with multiple stop hooks (using hooks dict)."""
- step = Step(
- id="test",
- name="Test Step",
- description="A test step",
- instructions_file="steps/test.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={
- "after_agent": [
- HookAction(prompt="Check criteria 1"),
- HookAction(script="hooks/validate.sh"),
- ]
- },
- )
- assert len(step.stop_hooks) == 2
- assert step.stop_hooks[0].is_prompt()
- assert step.stop_hooks[1].is_script()
-
- def test_step_from_dict_with_stop_hooks(self) -> None:
- """Test Step.from_dict parses stop_hooks array."""
- data = {
- "id": "test",
- "name": "Test Step",
- "description": "A test step",
- "instructions_file": "steps/test.md",
- "outputs": ["output.md"],
- "stop_hooks": [
- {"prompt": "Check quality criteria"},
- {"script": "hooks/run_tests.sh"},
- ],
- }
- step = Step.from_dict(data)
- assert len(step.stop_hooks) == 2
- assert step.stop_hooks[0].prompt == "Check quality criteria"
- assert step.stop_hooks[1].script == "hooks/run_tests.sh"
-
- def test_step_from_dict_without_stop_hooks(self) -> None:
- """Test Step.from_dict with no stop_hooks returns empty list."""
- data = {
- "id": "test",
- "name": "Test Step",
- "description": "A test step",
- "instructions_file": "steps/test.md",
- "outputs": ["output.md"],
- }
- step = Step.from_dict(data)
- assert step.stop_hooks == []
-
- def test_step_from_dict_with_hooks_structure(self) -> None:
- """Test Step.from_dict parses new hooks structure with lifecycle events."""
- data = {
- "id": "test",
- "name": "Test Step",
- "description": "A test step",
- "instructions_file": "steps/test.md",
- "outputs": ["output.md"],
- "hooks": {
- "after_agent": [
- {"prompt": "Check quality"},
- {"script": "hooks/validate.sh"},
- ],
- "before_tool": [
- {"prompt": "Pre-tool check"},
- ],
- },
- }
- step = Step.from_dict(data)
- # stop_hooks property returns after_agent hooks
- assert len(step.stop_hooks) == 2
- assert step.stop_hooks[0].prompt == "Check quality"
- assert step.stop_hooks[1].script == "hooks/validate.sh"
- # Check full hooks dict
- assert "after_agent" in step.hooks
- assert "before_tool" in step.hooks
- assert len(step.hooks["after_agent"]) == 2
- assert len(step.hooks["before_tool"]) == 1
-
-
-class TestSchemaValidation:
- """Tests for stop_hooks schema validation."""
-
- def test_valid_prompt_stop_hook(self) -> None:
- """Test schema accepts valid prompt stop hook."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "stop_hooks": [{"prompt": "Check quality"}],
- }
- ],
- }
- # Should not raise
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_valid_script_stop_hook(self) -> None:
- """Test schema accepts valid script stop hook."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "stop_hooks": [{"script": "hooks/validate.sh"}],
- }
- ],
- }
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_valid_prompt_file_stop_hook(self) -> None:
- """Test schema accepts valid prompt_file stop hook."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "stop_hooks": [{"prompt_file": "hooks/quality.md"}],
- }
- ],
- }
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_valid_multiple_stop_hooks(self) -> None:
- """Test schema accepts multiple stop hooks."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "stop_hooks": [
- {"prompt": "Check quality"},
- {"script": "hooks/tests.sh"},
- ],
- }
- ],
- }
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_invalid_stop_hook_missing_type(self) -> None:
- """Test schema rejects stop hook without type."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "stop_hooks": [{}], # Empty object
- }
- ],
- }
- with pytest.raises(ValidationError):
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_invalid_stop_hook_extra_fields(self) -> None:
- """Test schema rejects stop hook with extra fields."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "stop_hooks": [{"prompt": "Check", "extra": "field"}],
- }
- ],
- }
- with pytest.raises(ValidationError):
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_valid_hooks_with_after_agent(self) -> None:
- """Test schema accepts new hooks structure with after_agent event."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "hooks": {
- "after_agent": [{"prompt": "Check quality"}],
- },
- }
- ],
- }
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_valid_hooks_with_multiple_events(self) -> None:
- """Test schema accepts hooks with multiple lifecycle events."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "hooks": {
- "after_agent": [{"prompt": "Check quality"}],
- "before_tool": [{"script": "hooks/validate.sh"}],
- "before_prompt": [{"prompt": "Initialize context"}],
- },
- }
- ],
- }
- validate_against_schema(job_data, JOB_SCHEMA)
-
- def test_valid_hooks_with_script_action(self) -> None:
- """Test schema accepts hooks with script action."""
- job_data = {
- "name": "test_job",
- "version": "1.0.0",
- "summary": "Test job",
- "steps": [
- {
- "id": "step1",
- "name": "Step 1",
- "description": "A step",
- "instructions_file": "steps/step1.md",
- "outputs": ["output.md"],
- "hooks": {
- "before_tool": [{"script": "hooks/check.sh"}],
- },
- }
- ],
- }
- validate_against_schema(job_data, JOB_SCHEMA)
-
-
-class TestGeneratorStopHooks:
- """Tests for generator stop hooks context building."""
-
- @pytest.fixture
- def generator(self, tmp_path: Path) -> SkillGenerator:
- """Create generator with temp templates."""
- templates_dir = tmp_path / "templates"
- claude_dir = templates_dir / "claude"
- claude_dir.mkdir(parents=True)
-
- # Create minimal template
- template_content = """---
-description: {{ step_description }}
-{% if stop_hooks %}
-hooks:
- Stop:
- - hooks:
-{% for hook in stop_hooks %}
-{% if hook.type == "script" %}
- - type: command
- command: ".deepwork/jobs/{{ job_name }}/{{ hook.path }}"
-{% else %}
- - type: prompt
- prompt: "{{ hook.content }}"
-{% endif %}
-{% endfor %}
-{% endif %}
----
-# {{ job_name }}.{{ step_id }}
-{{ instructions_content }}
-"""
- (claude_dir / "skill-job-step.md.jinja").write_text(template_content)
- return SkillGenerator(templates_dir)
-
- @pytest.fixture
- def job_with_hooks(self, tmp_path: Path) -> JobDefinition:
- """Create job with stop hooks."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1 Instructions")
-
- return JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="A test job",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="First step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={
- "after_agent": [HookAction(prompt="Verify quality criteria")],
- },
- ),
- ],
- job_dir=job_dir,
- )
-
- @pytest.fixture
- def job_with_script_hook(self, tmp_path: Path) -> JobDefinition:
- """Create job with script stop hook."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1 Instructions")
-
- return JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="A test job",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="First step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={
- "after_agent": [HookAction(script="hooks/validate.sh")],
- },
- ),
- ],
- job_dir=job_dir,
- )
-
- @pytest.fixture
- def job_with_prompt_file_hook(self, tmp_path: Path) -> JobDefinition:
- """Create job with prompt file stop hook."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- hooks_dir = job_dir / "hooks"
- hooks_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1 Instructions")
- (hooks_dir / "quality.md").write_text("Check all quality criteria")
-
- return JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="A test job",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="First step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={
- "after_agent": [HookAction(prompt_file="hooks/quality.md")],
- },
- ),
- ],
- job_dir=job_dir,
- )
-
- def test_build_context_with_prompt_hook(
- self, generator: SkillGenerator, job_with_hooks: JobDefinition
- ) -> None:
- """Test context building includes prompt stop hook."""
- adapter = ClaudeAdapter()
- context = generator._build_step_context(job_with_hooks, job_with_hooks.steps[0], 0, adapter)
- assert "stop_hooks" in context
- assert len(context["stop_hooks"]) == 1
- assert context["stop_hooks"][0]["type"] == "prompt"
- assert context["stop_hooks"][0]["content"] == "Verify quality criteria"
-
- def test_build_context_with_script_hook(
- self, generator: SkillGenerator, job_with_script_hook: JobDefinition
- ) -> None:
- """Test context building includes script stop hook."""
- adapter = ClaudeAdapter()
- context = generator._build_step_context(
- job_with_script_hook, job_with_script_hook.steps[0], 0, adapter
- )
- assert "stop_hooks" in context
- assert len(context["stop_hooks"]) == 1
- assert context["stop_hooks"][0]["type"] == "script"
- assert context["stop_hooks"][0]["path"] == "hooks/validate.sh"
-
- def test_build_context_with_prompt_file_hook(
- self, generator: SkillGenerator, job_with_prompt_file_hook: JobDefinition
- ) -> None:
- """Test context building reads prompt file content."""
- adapter = ClaudeAdapter()
- context = generator._build_step_context(
- job_with_prompt_file_hook, job_with_prompt_file_hook.steps[0], 0, adapter
- )
- assert "stop_hooks" in context
- assert len(context["stop_hooks"]) == 1
- assert context["stop_hooks"][0]["type"] == "prompt_file"
- assert context["stop_hooks"][0]["content"] == "Check all quality criteria"
-
- def test_build_context_with_missing_prompt_file(
- self, generator: SkillGenerator, tmp_path: Path
- ) -> None:
- """Test error when prompt file is missing."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1")
-
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="out.md")],
- hooks={
- "after_agent": [HookAction(prompt_file="missing.md")],
- },
- )
- ],
- job_dir=job_dir,
- )
-
- adapter = ClaudeAdapter()
- with pytest.raises(GeneratorError, match="prompt file not found"):
- generator._build_step_context(job, job.steps[0], 0, adapter)
-
- def test_build_context_no_hooks(self, generator: SkillGenerator, tmp_path: Path) -> None:
- """Test context with no stop hooks."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1")
-
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="out.md")],
- )
- ],
- job_dir=job_dir,
- )
-
- adapter = ClaudeAdapter()
- context = generator._build_step_context(job, job.steps[0], 0, adapter)
- assert context["stop_hooks"] == []
-
- def test_build_context_multiple_hooks(self, generator: SkillGenerator, tmp_path: Path) -> None:
- """Test context with multiple stop hooks."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1")
-
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="out.md")],
- hooks={
- "after_agent": [
- HookAction(prompt="Check criteria 1"),
- HookAction(script="hooks/test.sh"),
- HookAction(prompt="Check criteria 2"),
- ],
- },
- )
- ],
- job_dir=job_dir,
- )
-
- adapter = ClaudeAdapter()
- context = generator._build_step_context(job, job.steps[0], 0, adapter)
- assert len(context["stop_hooks"]) == 3
- assert context["stop_hooks"][0]["type"] == "prompt"
- assert context["stop_hooks"][1]["type"] == "script"
- assert context["stop_hooks"][2]["type"] == "prompt"
-
- def test_build_context_duplicates_stop_to_subagent_stop(
- self, generator: SkillGenerator, job_with_hooks: JobDefinition
- ) -> None:
- """Test that Stop hooks are also registered for SubagentStop event.
-
- Claude Code has separate Stop and SubagentStop events. When a Stop hook
- is defined, it should also be registered for SubagentStop so the hook
- triggers for both the main agent and subagents.
- """
- adapter = ClaudeAdapter()
- context = generator._build_step_context(job_with_hooks, job_with_hooks.steps[0], 0, adapter)
-
- # Should have both Stop and SubagentStop in hooks dict
- assert "hooks" in context
- assert "Stop" in context["hooks"]
- assert "SubagentStop" in context["hooks"]
-
- # Both should have the same hooks
- assert context["hooks"]["Stop"] == context["hooks"]["SubagentStop"]
- assert len(context["hooks"]["Stop"]) == 1
- assert context["hooks"]["Stop"][0]["type"] == "prompt"
-
- def test_build_context_no_subagent_stop_without_stop(
- self, generator: SkillGenerator, tmp_path: Path
- ) -> None:
- """Test that SubagentStop is not created if there are no Stop hooks."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1")
-
- job = JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test",
- description="Test",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="Step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="out.md")],
- )
- ],
- job_dir=job_dir,
- )
-
- adapter = ClaudeAdapter()
- context = generator._build_step_context(job, job.steps[0], 0, adapter)
-
- # Should not have Stop or SubagentStop without any hooks
- assert "hooks" in context
- assert "Stop" not in context["hooks"]
- assert "SubagentStop" not in context["hooks"]
-
-
-class TestGeneratorTemplateOutput:
- """Tests for generated skill file output."""
-
- @pytest.fixture
- def full_generator(self) -> SkillGenerator:
- """Create generator using actual package templates."""
- # Use the actual templates directory from the package
- templates_dir = Path(__file__).parent.parent.parent / "src" / "deepwork" / "templates"
- return SkillGenerator(templates_dir)
-
- @pytest.fixture
- def job_with_quality_criteria(self, tmp_path: Path) -> JobDefinition:
- """Create job with quality_criteria for testing template output."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1 Instructions\n\nDo the thing.")
-
- return JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="A test job",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="First step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="output.md")],
- quality_criteria=["Criterion 1 is met", "Criterion 2 is verified"],
- ),
- ],
- job_dir=job_dir,
- )
-
- @pytest.fixture
- def job_with_stop_hooks(self, tmp_path: Path) -> JobDefinition:
- """Create job with custom stop hooks for testing template output."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1 Instructions")
-
- return JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="A test job",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="First step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={
- "after_agent": [HookAction(prompt="Custom validation prompt")],
- },
- ),
- ],
- job_dir=job_dir,
- )
-
- def test_template_generates_subagent_review_for_quality_criteria(
- self,
- full_generator: SkillGenerator,
- job_with_quality_criteria: JobDefinition,
- tmp_path: Path,
- ) -> None:
- """Test that template generates sub-agent review instructions for quality_criteria.
-
- NOTE: Prompt-based stop hooks don't work in Claude Code (issue #20221).
- Instead, quality_criteria generates sub-agent review instructions in content.
- """
- adapter = ClaudeAdapter()
- skill_path = full_generator.generate_step_skill(
- job_with_quality_criteria,
- job_with_quality_criteria.steps[0],
- adapter,
- tmp_path,
- )
-
- content = skill_path.read_text()
-
- # Should NOT generate Stop/SubagentStop hooks (prompt hooks disabled)
- assert "Stop:" not in content, "Prompt-based Stop hooks should not be generated"
- assert "SubagentStop:" not in content, (
- "Prompt-based SubagentStop hooks should not be generated"
- )
-
- # Should generate sub-agent review instructions in content
- assert "## Quality Validation" in content, "Quality Validation section should be generated"
- assert "sub-agent" in content.lower(), "Sub-agent review instructions should be present"
- assert "Criterion 1 is met" in content, "Quality criteria should be in content"
- assert "Criterion 2 is verified" in content, "Quality criteria should be in content"
-
- def test_template_does_not_generate_prompt_hooks(
- self, full_generator: SkillGenerator, job_with_stop_hooks: JobDefinition, tmp_path: Path
- ) -> None:
- """Test that template does NOT generate prompt-based stop hooks.
-
- NOTE: Prompt-based stop hooks don't work in Claude Code (issue #20221).
- The template should filter out prompt hooks and not generate them.
- """
- adapter = ClaudeAdapter()
- skill_path = full_generator.generate_step_skill(
- job_with_stop_hooks,
- job_with_stop_hooks.steps[0],
- adapter,
- tmp_path,
- )
-
- content = skill_path.read_text()
-
- # Should NOT generate Stop/SubagentStop hooks for prompt-type hooks
- assert "Stop:" not in content, "Prompt-based Stop hooks should not be generated"
- assert "SubagentStop:" not in content, (
- "Prompt-based SubagentStop hooks should not be generated"
- )
-
- # The prompt content should NOT appear in the hooks section
- assert "Custom validation prompt" not in content, (
- "Prompt content should not be in generated skill"
- )
-
- @pytest.fixture
- def job_with_script_hooks(self, tmp_path: Path) -> JobDefinition:
- """Create job with script-type stop hooks for testing template output."""
- job_dir = tmp_path / "test_job"
- job_dir.mkdir()
- steps_dir = job_dir / "steps"
- steps_dir.mkdir()
- (steps_dir / "step1.md").write_text("# Step 1 Instructions")
-
- return JobDefinition(
- name="test_job",
- version="1.0.0",
- summary="Test job",
- description="A test job",
- steps=[
- Step(
- id="step1",
- name="Step 1",
- description="First step",
- instructions_file="steps/step1.md",
- outputs=[OutputSpec(file="output.md")],
- hooks={
- "after_agent": [HookAction(script="hooks/validate.sh")],
- },
- ),
- ],
- job_dir=job_dir,
- )
-
- def test_template_generates_stop_hooks_for_script_type(
- self, full_generator: SkillGenerator, job_with_script_hooks: JobDefinition, tmp_path: Path
- ) -> None:
- """Test that template generates Stop/SubagentStop hooks for script-type hooks.
-
- Script-type hooks (type: command) still work in Claude Code, so they should be generated.
- """
- adapter = ClaudeAdapter()
- skill_path = full_generator.generate_step_skill(
- job_with_script_hooks,
- job_with_script_hooks.steps[0],
- adapter,
- tmp_path,
- )
-
- content = skill_path.read_text()
-
- # Should generate Stop and SubagentStop hooks for script-type hooks
- assert "Stop:" in content, "Script-based Stop hooks should be generated"
- assert "SubagentStop:" in content, "Script-based SubagentStop hooks should be generated"
-
- # Should contain the command type and path
- assert "type: command" in content, "Hook should have type: command"
- assert "hooks/validate.sh" in content, "Hook path should be in generated skill"
diff --git a/tests/unit/test_validation.py b/tests/unit/test_validation.py
index ccd31637..84664883 100644
--- a/tests/unit/test_validation.py
+++ b/tests/unit/test_validation.py
@@ -2,20 +2,19 @@
import pytest
-from deepwork.schemas.job_schema import JOB_SCHEMA
+from deepwork.schemas.workflow_schema import WORKFLOW_SCHEMA
from deepwork.utils.validation import ValidationError, validate_against_schema
class TestValidateAgainstSchema:
- """Tests for validate_against_schema function."""
+ """Tests for validate_against_schema function using workflow schema."""
- def test_validates_simple_job(self) -> None:
- """Test that validate_against_schema accepts valid simple job."""
- job_data = {
- "name": "simple_job",
+ def test_validates_simple_workflow(self) -> None:
+ """Test that validate_against_schema accepts valid simple workflow."""
+ workflow_data = {
+ "name": "simple_workflow",
"version": "1.0.0",
- "summary": "A simple job for testing",
- "description": "A simple job",
+ "summary": "A simple workflow for testing",
"steps": [
{
"id": "step1",
@@ -23,21 +22,19 @@ def test_validates_simple_job(self) -> None:
"description": "First step",
"instructions_file": "steps/step1.md",
"outputs": ["output.md"],
- "dependencies": [],
}
],
}
# Should not raise
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
- def test_validates_job_with_user_inputs(self) -> None:
- """Test validation of job with user input parameters."""
- job_data = {
- "name": "job_with_inputs",
+ def test_validates_workflow_with_user_inputs(self) -> None:
+ """Test validation of workflow with user input parameters."""
+ workflow_data = {
+ "name": "workflow_with_inputs",
"version": "1.0.0",
- "summary": "Job with user inputs",
- "description": "Job with inputs",
+ "summary": "Workflow with user inputs",
"steps": [
{
"id": "step1",
@@ -49,20 +46,18 @@ def test_validates_job_with_user_inputs(self) -> None:
{"name": "param2", "description": "Second parameter"},
],
"outputs": ["output.md"],
- "dependencies": [],
}
],
}
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
- def test_validates_job_with_file_inputs(self) -> None:
- """Test validation of job with file inputs from previous steps."""
- job_data = {
- "name": "job_with_deps",
+ def test_validates_workflow_with_file_inputs(self) -> None:
+ """Test validation of workflow with file inputs from previous steps."""
+ workflow_data = {
+ "name": "workflow_with_deps",
"version": "1.0.0",
- "summary": "Job with dependencies",
- "description": "Job with dependencies",
+ "summary": "Workflow with dependencies",
"steps": [
{
"id": "step1",
@@ -70,7 +65,6 @@ def test_validates_job_with_file_inputs(self) -> None:
"description": "First step",
"instructions_file": "steps/step1.md",
"outputs": ["data.md"],
- "dependencies": [],
},
{
"id": "step2",
@@ -84,28 +78,26 @@ def test_validates_job_with_file_inputs(self) -> None:
],
}
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
def test_raises_for_missing_required_field(self) -> None:
"""Test that validation fails for missing required fields."""
- job_data = {
- "name": "incomplete_job",
+ workflow_data = {
+ "name": "incomplete_workflow",
"version": "1.0.0",
# Missing summary
- # Missing description
"steps": [],
}
with pytest.raises(ValidationError, match="'summary' is a required property"):
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
- def test_raises_for_invalid_job_name(self) -> None:
- """Test that validation fails for invalid job name."""
- job_data = {
- "name": "Invalid-Job-Name", # Dashes not allowed
+ def test_raises_for_invalid_workflow_name(self) -> None:
+ """Test that validation fails for invalid workflow name."""
+ workflow_data = {
+ "name": "Invalid-Workflow-Name", # Dashes not allowed
"version": "1.0.0",
"summary": "Invalid name test",
- "description": "Invalid name",
"steps": [
{
"id": "step1",
@@ -118,15 +110,14 @@ def test_raises_for_invalid_job_name(self) -> None:
}
with pytest.raises(ValidationError, match="does not match"):
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
def test_raises_for_invalid_version(self) -> None:
"""Test that validation fails for invalid version format."""
- job_data = {
- "name": "job",
+ workflow_data = {
+ "name": "workflow",
"version": "1.0", # Not semver
"summary": "Invalid version test",
- "description": "Job",
"steps": [
{
"id": "step1",
@@ -139,28 +130,26 @@ def test_raises_for_invalid_version(self) -> None:
}
with pytest.raises(ValidationError, match="does not match"):
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
def test_raises_for_empty_steps(self) -> None:
"""Test that validation fails for empty steps array."""
- job_data = {
- "name": "job",
+ workflow_data = {
+ "name": "workflow",
"version": "1.0.0",
"summary": "Empty steps test",
- "description": "Job with no steps",
"steps": [],
}
with pytest.raises(ValidationError, match="should be non-empty"):
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
def test_raises_for_step_missing_outputs(self) -> None:
"""Test that validation fails for step without outputs."""
- job_data = {
- "name": "job",
+ workflow_data = {
+ "name": "workflow",
"version": "1.0.0",
"summary": "Missing outputs test",
- "description": "Job",
"steps": [
{
"id": "step1",
@@ -173,15 +162,14 @@ def test_raises_for_step_missing_outputs(self) -> None:
}
with pytest.raises(ValidationError, match="'outputs' is a required property"):
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
def test_raises_for_invalid_input_format(self) -> None:
"""Test that validation fails for invalid input format."""
- job_data = {
- "name": "job",
+ workflow_data = {
+ "name": "workflow",
"version": "1.0.0",
"summary": "Invalid input format test",
- "description": "Job",
"steps": [
{
"id": "step1",
@@ -200,14 +188,71 @@ def test_raises_for_invalid_input_format(self) -> None:
}
with pytest.raises(ValidationError):
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
- def test_validates_complex_job(self, fixtures_dir) -> None:
- """Test validation of complex job fixture."""
- from deepwork.utils.yaml_utils import load_yaml
+ def test_validates_workflow_with_hooks(self) -> None:
+ """Test validation of workflow with hooks."""
+ workflow_data = {
+ "name": "workflow_with_hooks",
+ "version": "1.0.0",
+ "summary": "Workflow with hooks",
+ "steps": [
+ {
+ "id": "step1",
+ "name": "Step 1",
+ "description": "Step with hooks",
+ "instructions_file": "steps/step1.md",
+ "outputs": ["output.md"],
+ "hooks": {
+ "after_agent": [{"script": "validate.sh"}],
+ },
+ }
+ ],
+ }
- complex_job_path = fixtures_dir / "jobs" / "complex_job" / "job.yml"
- job_data = load_yaml(complex_job_path)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
+
+ def test_validates_workflow_with_execution_order(self) -> None:
+ """Test validation of workflow with execution order including concurrent steps."""
+ workflow_data = {
+ "name": "concurrent_workflow",
+ "version": "1.0.0",
+ "summary": "Workflow with concurrent steps",
+ "steps": [
+ {
+ "id": "step1",
+ "name": "Step 1",
+ "description": "First step",
+ "instructions_file": "steps/step1.md",
+ "outputs": ["output1.md"],
+ },
+ {
+ "id": "step2",
+ "name": "Step 2",
+ "description": "Second step (parallel)",
+ "instructions_file": "steps/step2.md",
+ "outputs": ["output2.md"],
+ },
+ {
+ "id": "step3",
+ "name": "Step 3",
+ "description": "Third step (parallel)",
+ "instructions_file": "steps/step3.md",
+ "outputs": ["output3.md"],
+ },
+ {
+ "id": "step4",
+ "name": "Step 4",
+ "description": "Final step",
+ "instructions_file": "steps/step4.md",
+ "outputs": ["output4.md"],
+ },
+ ],
+ "execution_order": [
+ "step1",
+ ["step2", "step3"], # Concurrent
+ "step4",
+ ],
+ }
- assert job_data is not None
- validate_against_schema(job_data, JOB_SCHEMA)
+ validate_against_schema(workflow_data, WORKFLOW_SCHEMA)
diff --git a/uv.lock b/uv.lock
index 5c61745e..433ce43a 100644
--- a/uv.lock
+++ b/uv.lock
@@ -126,7 +126,7 @@ toml = [
[[package]]
name = "deepwork"
-version = "0.5.1"
+version = "0.6.0"
source = { editable = "." }
dependencies = [
{ name = "click" },
@@ -147,6 +147,16 @@ dev = [
{ name = "types-pyyaml" },
]
+[package.dev-dependencies]
+dev = [
+ { name = "mypy" },
+ { name = "pytest" },
+ { name = "pytest-cov" },
+ { name = "pytest-mock" },
+ { name = "ruff" },
+ { name = "types-pyyaml" },
+]
+
[package.metadata]
requires-dist = [
{ name = "click", specifier = ">=8.1.0" },
@@ -164,6 +174,16 @@ requires-dist = [
]
provides-extras = ["dev"]
+[package.metadata.requires-dev]
+dev = [
+ { name = "mypy", specifier = ">=1.0" },
+ { name = "pytest", specifier = ">=7.0" },
+ { name = "pytest-cov", specifier = ">=4.0" },
+ { name = "pytest-mock", specifier = ">=3.10" },
+ { name = "ruff", specifier = ">=0.1.0" },
+ { name = "types-pyyaml" },
+]
+
[[package]]
name = "gitdb"
version = "4.0.12"