Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "workflow-orchestrator",
"source": "./",
"description": "Delegation system with workflow orchestration, specialized agents, and parallel execution for Claude Code",
"version": "2.0.2",
"version": "2.0.3",
"author": {
"name": "Nadav Barkai"
},
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "workflow-orchestrator",
"version": "2.0.2",
"version": "2.0.3",
"description": "Delegation system with workflow orchestration, specialized agents, and parallel execution for Claude Code",
"author": {
"name": "Nadav Barkai"
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to this project will be documented in this file.

## [2.0.3] - 2026-04-16

### Fixed
- Restored main-agent delegation after f831fd2 regression: imperative nudge messages from violation #1, orchestrator stub gains work definition + MUST NOT list + self-catch directive, output style reframes main agent as orchestrator (not executor).

## [2.0.2] - 2026-04-12

### Added
Expand Down
9 changes: 4 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Delegation Policy (Soft Enforcement)

The framework nudges via stderr when the main agent uses work-doing tools (`Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `MultiEdit`, `NotebookEdit`) directly. **Nudges never block.** They escalate by per-turn violation count: silent → short hintwarning → strong reminder. The counter resets each turn and zeros when `/workflow-orchestrator:delegate` runs.
The framework nudges via stderr when the main agent uses work-doing tools (`Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `MultiEdit`, `NotebookEdit`) directly. **Nudges never block.** They escalate by per-turn violation count: silent → imperative STOPimperative STOP (2nd call phrasing) → strong reminder explaining what's being lost. The counter resets each turn and zeros when `/workflow-orchestrator:delegate` runs.

The expected path for any multi-step or work-shaped request is:

Expand Down Expand Up @@ -115,10 +115,9 @@ There is no allowlist. `require_delegation.py` tracks per-turn direct work-tool
| Violations | Message | Tokens |
|---|---|---|
| 0 | (silent) | 0 |
| 1 | `delegate?` | ~2 |
| 2 | `nudge: use /workflow-orchestrator:delegate for multi-step work` | ~12 |
| 3–4 | `WARNING: N direct tool calls bypassing delegation. Use /workflow-orchestrator:delegate <task>.` | ~25 |
| 5+ | Strong reminder explaining what's being lost | ~55 |
| 1 | `STOP. This tool call bypasses delegation. Abandon this step and run: /workflow-orchestrator:delegate <your task>` | ~25 |
| 2 | `STOP. 2nd direct tool call this turn. The main agent does not execute work tools. Run: /workflow-orchestrator:delegate <your task>` | ~28 |
| 3+ | `STOP. N direct tool calls bypassing delegation. You are losing planning, parallelization, and context isolation. Abandon the current plan and run: /workflow-orchestrator:delegate <your task>` | ~55 |

Tracked tools (the only ones that count as violations): `Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `MultiEdit`, `NotebookEdit`. These 8 stable primitives are the only ones monitored. New Claude Code tools never trigger nudges.

Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,20 +193,21 @@ and then prompt claude with:
The delegation system uses adaptive nudges instead of hard blocks:

```bash
# Turn 1: Direct tool call (silent)
# 1st direct tool call (imperative STOP)
Read test.py
# stderr: "STOP. This tool call bypasses delegation. Abandon this step and run: /workflow-orchestrator:delegate <your task>"

# Turn 2: Another direct tool call (hint)
# 2nd direct tool call (imperative STOP, 2nd-call phrasing)
Read other.py
# stderr: "delegate?"
# stderr: "STOP. 2nd direct tool call this turn. The main agent does not execute work tools. Run: /workflow-orchestrator:delegate <your task>"

# Turn 3: Third direct tool call (nudge)
# 3rd direct tool call (strong reminder — explains what's being lost)
Edit file.py
# stderr: "nudge: use /workflow-orchestrator:delegate for multi-step work"
# stderr: "STOP. 3 direct tool calls bypassing delegation. You are losing planning, parallelization, and context isolation. Abandon the current plan and run: /workflow-orchestrator:delegate <your task>"

# Turn 4: Fourth direct tool call (warning)
# 4th+ direct tool calls keep the same strong reminder with the updated count
Bash command.sh
# stderr: "WARNING: 3 direct tool calls bypassing delegation. Use /workflow-orchestrator:delegate <task>."
# stderr: "STOP. 4 direct tool calls bypassing delegation. ..."

# Delegation resets the counter (state clean)
/workflow-orchestrator:delegate "Create feature"
Expand Down
17 changes: 9 additions & 8 deletions hooks/PreToolUse/require_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,19 @@ def message_for(count: int) -> str | None:
if count <= 0:
return None
if count == 1:
return "delegate?"
return (
"STOP. This tool call bypasses delegation. Abandon this step "
"and run: /workflow-orchestrator:delegate <your task>"
)
if count == 2:
return "nudge: use /workflow-orchestrator:delegate for multi-step work"
if count <= 4:
return (
f"WARNING: {count} direct tool calls bypassing delegation. "
"Use /workflow-orchestrator:delegate <task>."
"STOP. 2nd direct tool call this turn. The main agent does not "
"execute work tools. Run: /workflow-orchestrator:delegate <your task>"
)
return (
f"WARNING: {count}+ direct tool calls. The orchestrator plans, "
"parallelizes, and isolates context — direct use loses these benefits. "
"Run /workflow-orchestrator:delegate <task> now."
f"STOP. {count} direct tool calls bypassing delegation. You are "
"losing planning, parallelization, and context isolation. Abandon "
"the current plan and run: /workflow-orchestrator:delegate <your task>"
)
Comment thread
qodo-code-review[bot] marked this conversation as resolved.


Expand Down
4 changes: 3 additions & 1 deletion output-styles/technical-adaptive.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ keep-coding-instructions: true

# Response Format

The main agent is an orchestrator, not an executor. Work goes through /workflow-orchestrator:delegate. Responses summarize subagent output — they do not describe tool calls the main agent made directly.

## Default Mode: Ultra Concise

- Deliver only what is necessary. No fluff, no preamble, no recap.
- Expert-level brevity — assume the user has deep technical knowledge.
- Direct answers only. No hand-holding.
- After completing an edit, respond with ONE sentence (e.g., "Done. Added timeout to compact_run.py."). The user can read the diff.
- After a delegation completes, respond with ONE sentence summarizing what the subagents did (e.g., "Done. Wave 0 added timeout to compact_run.py."). The user can read the diffs and task log.
- Never restate the user's problem before acting.
- Never reply "You're absolutely right" or similar affirmations — just act.
- Never add time or effort estimates to tasks.
Expand Down
19 changes: 18 additions & 1 deletion system-prompts/orchestrator_stub.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,29 @@ Any user request that requires work — writing code, running tools, multi-step
/workflow-orchestrator:delegate <full task description>
```

The main agent does not execute work tools directly. Use only: Tasks API, AskUserQuestion, and `/workflow-orchestrator:delegate`. The delegate command loads the full orchestrator (planning, agent assignment, execution waves) on demand.
The main agent does not execute work tools directly. Use only: Tasks API, AskUserQuestion, `/workflow-orchestrator:delegate`, and `/workflow-orchestrator:ask` (read-only questions). The delegate command loads the full orchestrator (planning, agent assignment, execution waves) on demand.

## Exception — continuation after plan approval

If you received a "PLAN ALREADY APPROVED" or "continuing to STAGE 1" continuation message from the Stop hook, **do NOT re-invoke `/workflow-orchestrator:delegate`** and **do NOT call `EnterPlanMode`** again. The orchestrator is already loaded and the plan is already approved — proceed directly to Stage 1 execution by rendering the dependency graph and spawning Wave 0 agents. In this exception path, the `Agent` tool (plus `TaskCreate`/`TaskUpdate`/`TaskGet` for status, and `TeamCreate`/`SendMessage` if running in team mode) is permitted — these are how Wave 0 phases are spawned. The "all work → delegate" rule above does NOT apply during in-flight delegation continuation.

## What counts as "work"

ANY of these = delegate:
- Reading, searching, editing, or writing files (Read, Grep, Glob, Edit, Write, MultiEdit, NotebookEdit)
- Running shell commands (Bash) for anything beyond a single trivial status check
- Investigating the codebase to answer a question that requires file access → use /workflow-orchestrator:ask
- Multi-step tasks, even if each step looks simple in isolation

## What you MUST NOT do

- Do NOT open files with Read to "just check" before deciding — delegate the check
- Do NOT run `grep`/`find`/`ls` via Bash — delegate
- Do NOT make "just a small edit" directly — delegate
- Do NOT chain 2+ tool calls to accomplish one user request — delegate

If you catch yourself about to call Bash/Edit/Write/Read/Glob/Grep/MultiEdit/NotebookEdit, STOP and invoke `/workflow-orchestrator:delegate <task>` instead.

## Team Mode

If `TeamCreate` is in your available tools, agent teams are enabled. When you run `/workflow-orchestrator:delegate`, default to team mode (`TeamCreate` + `Agent(team_name=...)`) for multi-agent work. If `TeamCreate` is not available, the delegate flow falls back to parallel subagents automatically.
Expand Down
34 changes: 21 additions & 13 deletions tests/test_require_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,46 +110,54 @@ def test_non_work_tools_silent(self, tmp_path: Path, tool: str) -> None:


class TestEscalationLadder:
def test_first_violation_is_short(self, tmp_path: Path) -> None:
def test_first_violation_is_imperative(self, tmp_path: Path) -> None:
_, stderr, _ = _run(_input("Bash"), tmp_path)
assert "delegate?" in stderr # noqa: S101
# Short message — under ~30 chars
assert len(stderr.strip()) < 30 # noqa: S101
# New imperative message must include STOP + the delegate command
assert "STOP" in stderr # noqa: S101
assert "/workflow-orchestrator:delegate" in stderr # noqa: S101

def test_second_violation_is_medium(self, tmp_path: Path) -> None:
_run(_input("Bash"), tmp_path)
_, stderr, _ = _run(_input("Edit"), tmp_path)
assert "nudge" in stderr # noqa: S101
assert "STOP" in stderr # noqa: S101
assert "/workflow-orchestrator:delegate" in stderr # noqa: S101
# Distinct 2nd-call phrasing
assert "2nd direct tool call" in stderr # noqa: S101

def test_third_violation_is_warning(self, tmp_path: Path) -> None:
for tool in ("Bash", "Edit", "Write"):
_run(_input(tool), tmp_path)
_, stderr, _ = _run(_input("Read"), tmp_path)
# 4th call -> "WARNING: 4 direct tool calls"
assert "WARNING" in stderr # noqa: S101
# 4th call -> "STOP. 4 direct tool calls bypassing delegation..."
assert "STOP" in stderr # noqa: S101
assert "4" in stderr # noqa: S101
assert "/workflow-orchestrator:delegate" in stderr # noqa: S101

def test_fifth_plus_is_strong(self, tmp_path: Path) -> None:
for _ in range(5):
_run(_input("Bash"), tmp_path)
_, stderr, _ = _run(_input("Bash"), tmp_path) # 6th call
assert "WARNING" in stderr # noqa: S101
assert "parallelizes" in stderr # noqa: S101
assert "STOP" in stderr # noqa: S101
# The ≥3 message explains what's being lost
assert "losing planning, parallelization, and context isolation" in stderr # noqa: S101
# Long message — over ~100 chars
assert len(stderr.strip()) > 100 # noqa: S101

def test_message_length_grows(self, tmp_path: Path) -> None:
"""Verify token cost (proxied by message length) scales with violations."""
"""Verify escalation adds context (proxied by message length/content)."""
lengths = []
messages = []
for _ in range(6):
_, stderr, _ = _run(_input("Bash"), tmp_path)
lengths.append(len(stderr.strip()))
# Each level should be at least as long as the previous
messages.append(stderr.strip())
# Each level should be at least as long as the previous (monotonic)
for i in range(1, len(lengths)):
assert lengths[i] >= lengths[i - 1] # noqa: S101
# The final message should be substantially longer than the first
assert lengths[-1] > lengths[0] * 5 # noqa: S101
# The final message should be strictly longer than the first
assert lengths[-1] > lengths[0] # noqa: S101
# And must include the "what's being lost" context that proves escalation
assert "losing planning, parallelization, and context isolation" in messages[-1] # noqa: S101


# ---------------------------------------------------------------------------
Expand Down
Loading