Skip to content

Latest commit

 

History

History
332 lines (243 loc) · 16.2 KB

File metadata and controls

332 lines (243 loc) · 16.2 KB

Sub-Orchestrator Pattern

When the work exceeds what a flat master->agent dispatch can handle, the master delegates orchestration itself. A sub-orchestrator is an agent whose job is not to do work, but to manage agents that do work.

When to Use This Pattern

Scenario Flat dispatch (master->agents) Sub-orchestrator
5-15 independent tasks Yes Overkill
15-30 tasks, same type Maybe (wave dispatching) Better
30+ tasks, same type No (master context bloats) Yes
Mixed task types (audit + fix + validate) Yes (one per type) Per-type sub-orchestrator
Tasks with complex inter-dependencies Case by case Yes (orchestrator manages ordering)

Rule of thumb: If you need more than 2 waves of dispatch, or the tracking logic itself is complex, promote the tracking to a sub-orchestrator.

Architecture

MASTER (thin -- routes, approves, reviews)
  |
  |-- SUB-ORCHESTRATOR (manages N workers in batches)
  |     |-- Worker Agent 1
  |     |-- Worker Agent 2
  |     |-- ...
  |     |-- Worker Agent N
  |     |
  |     |-- Writes: progress log (.md file)
  |     |-- Returns: summary + log path + aggregate scores
  |
  |-- (other agents or sub-orchestrators)
  |
  MASTER: reviews summary, approves, next action

Role Separation

Role Reads files? Writes code? Manages agents? Talks to owner? Heavy lifting?
Master No (summaries only) No Yes (dispatches sub-orchestrators) Yes None -- routes and approves
Sub-orchestrator Index file only No Yes (manages worker pool) No (returns to master) Aggregation only
Worker agent Yes (all of them) Yes (if needed) No No (returns to orchestrator) ALL of it

The deeper you go, the more work is assigned. Master decides WHAT. Sub-orchestrator manages WHEN and HOW MANY. Workers do ALL the actual work -- reading, analyzing, fixing, writing reports, building summaries. Results flow up as thin pointers and executive summaries. Detail stays at the bottom in files on disk.

The Pattern in Detail

1. Master Dispatches Sub-Orchestrator

Master defines:

  • What: the overall goal (e.g., "score and fix all 77 blog posts")
  • Rules: quality criteria, scoring rubric, pass/fail thresholds
  • Batch size: how many workers to run concurrently (default: 10)
  • Log file: path where the sub-orchestrator writes progress
  • Return format: what master needs back (summary stats, category breakdown, log path)
Master prompt to sub-orchestrator:

You are a sub-orchestrator managing blog post quality scoring.

GOAL: Score all 77 blog posts against the quality rubric. Fix any scoring below 85.
RUBRIC: [SEO score, AEO score, Quality score -- all must be 85+]
POSTS: [list of 77 file paths]
BATCH SIZE: 10 concurrent workers
LOG FILE: <workspace-path>/<project>/.logs/quality-audit-2026-03-26.md
DIVERGENT MODE: AUTO

Instructions:
1. Dispatch workers in batches of 10
2. Each worker scores one post, returns: filename, SEO/AEO/Quality scores, pass/fail, issues found
3. If a post fails (<85 on any metric): dispatch a fix agent, then re-score
4. Log every result to the log file as workers complete (append, don't overwrite)
5. After all 77 are done, aggregate results and return summary to master

Return format:
STATUS: OK (or PARTIAL if some posts couldn't be fixed)
LOG: [path to log file]
SUMMARY:
  Total: 77 | Pass: 63 | Fixed: 11 | Failed: 3
  By category: [table]
  Lowest scores: [bottom 5]
FILES_CHANGED: [list of modified post files]

2. Sub-Orchestrator Manages Batches

The sub-orchestrator maintains a rolling pool of concurrent workers:

remaining = [all 77 posts]
active = []
max_concurrent = 10

# Initial fill -- launch up to max_concurrent
while len(active) < max_concurrent and remaining is not empty:
    task = remaining.pop(0)
    agent = dispatch worker (background)
    active.append(agent)

# Rolling loop -- as each completes, backfill immediately
while active is not empty:
    completed = wait for any one agent to complete

    log result to .md file
    if failed:
        dispatch fix agent, re-score, log fix result

    active.remove(completed)

    if remaining is not empty:
        task = remaining.pop(0)
        agent = dispatch worker (background)
        active.append(agent)

This is a rolling pool, not batch-and-wait. The sub-orchestrator launches 10 workers initially. When worker #1 finishes, worker #11 launches immediately -- there are always 10 running (until the remaining queue empties). This maximizes throughput because fast workers don't wait for slow ones in the same batch.

Key behaviors:

  • Maintains max_concurrent active workers at all times (rolling, not batch-and-wait)
  • Logs results as they arrive (progress is visible even if sub-orchestrator crashes)
  • Fix-and-rescore happens inline when a worker completes, before backfilling the slot
  • Sub-orchestrator itself stays thin -- it reads worker summaries, not file content

Sizing max_concurrent: The right pool size depends on the machine's resources and the work each agent does:

  • CPU/memory: Each agent consumes runtime memory. Monitor system resources and reduce pool size if the machine is under pressure.
  • Agent weight: Lightweight agents (score one file, return 10 lines) can run 10-15 concurrently. Heavy agents (read 20+ files, write code, run tests) may need a pool of 3-5.
  • Claude Code heap: The sub-orchestrator's own context grows with each result it processes. Workers should write heavy output to files and return only a pointer + executive summary (see "Write to File, Return the Pointer" below).
  • Start conservative: Begin with 5, observe resource usage, scale up. Better to run 8 smoothly than 15 with thrashing.

3. Worker Agents Do the Actual Work

Core principle: the deeper you go, the more work is assigned. Workers do ALL the heavy lifting -- reading files, analyzing, fixing, writing reports, building summaries. The orchestrator layer above them should never have to dig into details to make routing decisions.

Each worker gets a self-contained prompt:

Score this blog post against the quality rubric.

FILE: <workspace-path>/<project>/content-drafts/ai-security-basics.md
RUBRIC:
  SEO: [criteria]
  AEO: [criteria]
  Quality: [criteria]

Write your full analysis to: <workspace-path>/<project>/.logs/scores/ai-security-basics.md
Start the file with an EXECUTIVE SUMMARY section (scores, pass/fail, issues -- everything
the orchestrator needs to act without reading the rest of the file).
Append the log index file: <workspace-path>/<project>/.logs/quality-audit-2026-03-26.md

Return to orchestrator (keep under 5 lines):
STATUS: OK
SCORES: SEO=87 AEO=91 Quality=83
PASS: false (Quality below 85)
DETAIL: .logs/scores/ai-security-basics.md

Workers are disposable. They read files, do the analysis, write the output to disk, update the shared log, and return a minimal pointer. Their context is fully consumed and freed.

Write to File, Return the Pointer

This is the most important pattern for keeping orchestrator context thin.

Workers and sub-orchestrators should never pass heavy output up the chain. Instead:

  1. Worker writes full results to an .md file -- analysis, findings, fix details, everything
  2. Worker writes an executive summary as the FIRST section of that file -- scores, pass/fail, action needed. The orchestrator can read just this section if it needs more than the return line.
  3. Worker appends a one-line entry to a shared index/log file -- the sub-orchestrator reads this file for aggregate status without collecting individual returns
  4. Worker returns to orchestrator: status + scores + file path -- 3-5 lines max
Output hierarchy (worker produces all three):

1. Full report:    .logs/scores/ai-security-basics.md     (owner reads later)
   [EXECUTIVE SUMMARY at top -> full analysis below]

2. Index entry:    .logs/quality-audit-2026-03-26.md       (orchestrator reads for aggregate)
   | ai-security-basics.md | SEO=87 | AEO=91 | Quality=83 | FAIL | .logs/scores/ai-security-basics.md |

3. Return message: STATUS: OK / SCORES / PASS / DETAIL path  (orchestrator receives directly)
   [3-5 lines, just enough to route]

Why the executive summary matters: If the sub-orchestrator needs to make a decision about a worker's output (retry? escalate? skip?), it reads the executive summary section of the output file -- NOT the full report, and NOT a verbose return message. The worker already did the thinking and distilled it. The orchestrator just acts on the distillation.

Why the index file matters: The sub-orchestrator doesn't need to collect and assemble results from 77 individual returns. Workers append to a shared log as they complete. When all workers finish, the log IS the aggregate report. The sub-orchestrator reads it once, builds the final summary for master, done.

This pattern scales to any depth. A sub-orchestrator returning to master follows the same rule: write the detailed aggregate to a file, return the pointer + executive summary.

4. Sub-Orchestrator Returns to Master

The sub-orchestrator follows the same "write to file, return the pointer" pattern. Workers already built the index file as they ran. The sub-orchestrator reads that index, builds an executive summary, writes the detailed aggregate report to disk, and returns a thin summary to master.

After all 77 posts are processed:

STATUS: OK
LOG: <log-path>/quality-audit-2026-03-26.md
SUMMARY:
  Total: 77 | Pass: 63 | Fixed: 11 | Failed: 3

  By category:
  | Category    | Count | Pass | Fixed | Failed | Avg SEO | Avg AEO | Avg Quality |
  |-------------|-------|------|-------|--------|---------|---------|-------------|
  | Product A   | 15    | 12   | 3     | 0      | 91      | 89      | 88          |
  | Product B   | 12    | 10   | 2     | 0      | 88      | 87      | 86          |
  | Product C   | 10    | 8    | 1     | 1      | 85      | 84      | 82          |
  | Product D   | 8     | 5    | 2     | 1      | 83      | 86      | 84          |
  | Product E   | 6     | 5    | 1     | 0      | 90      | 92      | 89          |
  | Product F   | 26    | 23   | 2     | 1      | 86      | 85      | 85          |

  Failed posts (could not reach 85+):
  1. post-with-structural-issues.md -- Quality=79 (structural issues)
  2. post-missing-key-points.md -- AEO=78 (missing key_points)
  3. post-with-keyword-stuffing.md -- SEO=74 (keyword stuffing)

FILES_CHANGED:
  - [list of 11 modified post files]

Master reviews the summary, decides what to do about the 3 failures (manual fix, different approach, or accept).

The Log File

The sub-orchestrator's log file is critical. It serves three purposes:

  1. Progress visibility: Owner can read it mid-run to see how things are going
  2. Crash recovery: If the sub-orchestrator dies, the log shows what completed
  3. Audit trail: Post-run record of every score, fix, and decision

Log Format

# Quality Audit Log
**Started:** 2026-03-26 14:00
**Rubric:** SEO/AEO/Quality, threshold 85+
**Total posts:** 77

## Batch 1 (posts 1-10)
| # | File | SEO | AEO | Quality | Pass | Action |
|---|------|-----|-----|---------|------|--------|
| 1 | ai-security-basics.md | 87 | 91 | 83 | FAIL | Fixed -> 87 |
| 2 | aibm-marina-marketing.md | 92 | 88 | 90 | PASS | -- |
| 3 | ... | ... | ... | ... | ... | ... |

## Batch 2 (posts 11-20)
...

## Summary
[populated after all batches complete]

Divergent Thinking Integration

Sub-orchestrators follow the divergent mode rules:

  • Workers: Always serial. They score one file against a rubric -- no ambiguity, no multi-path evaluation needed.
  • Sub-orchestrator: Serial for batch management. Divergent mode doesn't add value to "dispatch 10, collect, dispatch 10 more."
  • Master: Divergent when deciding WHAT to do with the results (fix strategy, priority order, which failures to accept vs retry).

The divergent mode prompt (DIVERGENT MODE: <MODE>) passes from master to sub-orchestrator to worker only when the task type warrants it. Scoring against a rubric doesn't warrant it. Deciding how to fix a failing post might.

Nested Sub-Orchestrators

For very large operations, sub-orchestrators can dispatch their own sub-orchestrators:

MASTER
  |-- Sub-Orchestrator: "Audit all 6 content categories"
        |-- Sub-Orchestrator: "Audit category-A posts (15)"
        |     |-- Workers (batches of 10)
        |-- Sub-Orchestrator: "Audit category-B posts (12)"
        |     |-- Workers (batches of 10)
        |-- ...

Use sparingly. Each nesting level adds dispatch overhead and return aggregation complexity. Two levels (master -> sub-orch -> workers) handles most real workloads. Three levels is the practical maximum.

When NOT to Use Sub-Orchestrators

  • Less than 15 tasks: Flat dispatch from master is simpler and faster
  • Tasks require owner decisions mid-stream: Use flat dispatch so master can ask the owner between agents
  • Tasks are highly heterogeneous: Different task types with different prompts are better as separate flat dispatches
  • Debugging: When something is going wrong, flatten the hierarchy so you can see each agent's behavior directly

Comparison to Flat Dispatch

Aspect Flat (master->agents) Sub-orchestrator
Master context cost O(N) -- one dispatch + result per task O(1) -- one dispatch + result total
Tracking complexity Master tracks all N tasks Sub-orchestrator tracks, master sees summary
Crash recovery Master re-dispatches failed agents Sub-orchestrator handles retries internally
Owner visibility Owner sees each result as it arrives Owner sees summary after completion (log file for mid-run visibility)
Batch management Master manages waves manually Sub-orchestrator manages waves automatically
Latency Lower for small N (no orchestrator overhead) Lower for large N (master doesn't bottleneck)

Real-World Examples

Content Quality Audit (77 blog posts)

Problem: 77 blog posts needed scoring against SEO/AEO/Quality rubric. Posts below 85 needed automated fixes. Owner wanted category-level reporting.

Solution: Master dispatched one sub-orchestrator with the full post list, rubric, and batch size of 10. Sub-orchestrator managed 8 batches of workers. Each worker scored one post. Failed posts got a fix-and-rescore cycle within the same batch. Sub-orchestrator maintained a log file throughout. After completion, returned aggregate scores by category with visual breakdown.

Result: 63 passed, 11 auto-fixed to passing, 3 required manual attention. Owner reviewed the summary and log file, spent 15 minutes on the 3 failures instead of hours on all 77.

Cross-Repo Dependency Audit

Problem: 22 repos needed checking for outdated dependencies, security advisories, and version alignment.

Solution: Master dispatched a sub-orchestrator with the repo list. Sub-orchestrator ran workers in batches of 8 (lighter than content scoring -- no file writes). Each worker ran npm audit, checked package.json versions against the tech stack standard, and returned a risk score. Sub-orchestrator aggregated by severity.

Multi-Vertical Content Generation

Problem: Generate draft content across 6 business verticals, each with different voice, audience, and keyword targets.

Solution: Master dispatched 6 sub-orchestrators (one per vertical). Each sub-orchestrator managed its own batch of content-generation workers. Each worker drafted one post. Sub-orchestrators scored drafts against vertical-specific rubrics and fixed below-threshold content. Master received 6 summaries with per-vertical quality metrics.

Implementation Checklist

When setting up a sub-orchestrator:

  • Define the goal (one sentence)
  • Define the worker prompt template (what each worker does)
  • Define the scoring/success criteria
  • Set batch size (default 10, lower for heavy workers, higher for lightweight)
  • Define log file path and format
  • Define the return summary format (what master needs)
  • Define fix-and-retry behavior (auto-fix? how many retries? escalation threshold?)
  • Include DIVERGENT MODE in the prompt chain if applicable
  • Test with 2-3 items before scaling to full set