drivers package

Reviewed against commit 8a2cb42ad on 2026-10-23.

Reference: package page (12 modules, 2,784 lines) · key modules: driver_factory, base, claude_code_driver, deepseek_coder_driver, browser_driver, claude_settings. The drivers serve the junior and CodeDirector volks; see the overview and the agents page.

The story

Orchestrata's Junior volks don't write code themselves — they delegate to external code-generation tools. The problem is that those tools are wildly heterogeneous: some are agentic CLIs that write files straight to disk (Claude Code, Qwen Code), some are non-interactive command-line assistants (Aider), some are plain LLM APIs whose text output has to be parsed into files (DeepSeek), and some are free-tier chat web UIs that can only be reached by driving a headless browser (Qwen Chat, DeepSeek Chat, Kimi). orchestrata_ultimate.drivers exists to hide all of that behind one uniform async contract, so that CodeDirector's swarm coordinator can fan out parallel per-file generation tasks without caring which backend does the work.

The contract lives in base.py as AsyncCodeDriver, a @runtime_checkable Protocol traced to SPEC-025. It declares exactly one coroutine — execute(task, files, is_fix) -> (success, output) — and every concrete driver in the package implements it structurally: none of them subclass anything, they simply provide a matching execute. Callers never construct drivers directly; they ask driver_factory.get_driver(volk) for one, naming only a volk. The factory reads that volk's configured provider from llm.config.get_volk_provider, then maps the provider string to the right concrete class through per-backend constructors (_get_claude_driver, _get_claude_code_driver, _get_deepseek_driver, _get_aider_driver, _get_qwen_driver, _get_browser_driver), falling back to the Claude API driver when a provider is unknown. This consolidates driver-selection logic that was once duplicated across the spike's swarm, codedirector, and junior modules.

From there a generation request follows one of three shapes. The CLI drivers (aider_driver, claude_code_driver, claude_api_driver, qwen_driver) locate their binary via an environment override or shutil.which, read a timeout from the environment, and run the tool as an async subprocess guarded by asyncio.wait_for. The agentic ones among them (Claude Code piped over stdin, Qwen with --yolo -p -) let the tool write files itself and then walk the workspace (_scan_source_files) to report what was created. The API-shaped deepseek_coder_driver skips external tooling entirely: it calls the model through llm.client.call_llm, parses FILE: blocks out of the raw response, writes and syntax-checks the files, auto-fixes lint with ruff, and retries — first with a stricter prompt when extraction comes up empty, then to recover any files the design expected but the model omitted. The browser-shaped browser_driver pilots Playwright headless Chromium against chat web UIs, with provider specifics (URLs, CSS selectors, rate limits, session budgets) externalized into browser_providers.py so the driver itself stays generic.

The package's neighbours are few and deliberate: deepseek_coder_driver reaches into orchestrata_ultimate.llm.client for the actual model call, the factory reads driver configuration from llm.config, and every module carries @traces decorators from core.traceability linking it back to its SPEC-025 requirements. Response parsing that both the browser and API paths need — FILE: blocks and markdown code fences — is shared in extraction.py. Upstream, the package's consumers are the CodeDirector/Junior agents, which see nothing but get_driver() and the AsyncCodeDriver protocol.

Key responsibilities

Class diagram

The package is protocol-centric rather than inheritance-heavy. AsyncCodeDriver is a @runtime_checkable Protocol (shown here as a plain class, since the renderer rejects the <<Protocol>> stereotype); every concrete driver conforms to it structurally — the dashed "realization" arrows below mean "implements the protocol", not "subclasses it".

The contract and the subprocess (CLI) drivers

AiderDriver, ClaudeCodeDriver, ClaudeAPIDriver, and QwenDriver all wrap an external binary as an async subprocess. They differ mainly in how tool output becomes files: Aider and Claude Code stream stdout back as text, while ClaudeAPIDriver and QwenDriver let the agentic tool write to disk and then scan the workspace.

Diagram projects-orchestrata-ultimate-modules-drivers-1

The direct-from-LLM driver

DeepSeekCoderDriver needs no external tool. It calls llm.client.call_llm and turns the raw text into files itself, with a rich chain of private helpers for extraction, retry, and missing-file recovery.

Diagram projects-orchestrata-ultimate-modules-drivers-2

The browser driver and its provider config

BrowserDriver drives Playwright headless Chromium against a free-tier chat UI. Everything provider-specific — URLs, CSS selectors, rate limits, session budgets — lives in the frozen BrowserProviderConfig dataclass it composes (_config), so one generic driver serves Qwen Chat, DeepSeek Chat, and Kimi.

Diagram projects-orchestrata-ultimate-modules-drivers-3

Module dependency diagram

Internal module imports within orchestrata_ultimate.drivers (generated from the AST, render-verified):

Diagram projects-orchestrata-ultimate-modules-drivers-4

Module inventory

Module Purpose
__init__.py Package init; exports the driver protocol, BrowserDriver, browser provider configs, extraction helpers, and the get_driver factory (REQ-025-088).
base.py The driver contract — AsyncCodeDriver, a runtime-checkable Protocol with a single execute() coroutine, traced to SPEC-025.
aider_driver.py Async Aider CLI driver: runs aider --message --yes-always --no-auto-commits --no-git as an async subprocess with a DeepSeek backend (300s default timeout).
claude_code_driver.py Async Claude Code CLI driver: runs claude --print --dangerously-skip-permissions, streaming stdout; timeout via asyncio.wait_for (900s default).
claude_api_driver.py Agentic Claude Code driver: pipes the prompt over stdin (avoiding "argument list too long" on large prompts) and scans the workspace for created files afterward (1200s default).
qwen_driver.py Async Qwen Code CLI driver: runs qwen --yolo -p - with a stdin pipe; agentic tool writes files directly, then the workspace is scanned (1200s default).
deepseek_coder_driver.py Direct-from-LLM driver (no external CLI): calls llm.client.call_llm, parses FILE: blocks, validates syntax, auto-fixes lint, retries on empty extraction and recovers missing design files.
browser_driver.py Playwright headless-Chromium driver against free-tier LLM web UIs (Qwen Chat, DeepSeek Chat, Kimi): rate-limiting, session-expiry checks, persisted login state, and escalating session recovery.
browser_providers.py BrowserProviderConfig frozen dataclass + the BROWSER_PROVIDERS registry + get_provider_config(): externalizes URLs, CSS selectors, and limits so BrowserDriver stays generic.
claude_settings.py Claude Code settings generation for the SPEC-025 governance gate: write_governance_settings writes .claude/orchestrata-settings.json in a workspace, registering a PreToolUse hook that runs the governance gate script; claude_code_driver passes the path with --settings. (Missing from the earlier inventory.)
driver_factory.py get_driver(volk) — maps a volk's configured provider to the right concrete driver via private per-backend constructors, with a Claude-API fallback.
extraction.py Shared LLM-response parsers: extract_file_blocks (FILE: blocks) and extract_markdown_code_blocks (fenced code), reused by the browser and API drivers.

base.py — the driver contract

base.py defines AsyncCodeDriver, a @runtime_checkable Protocol traced to SPEC-025. It is the keystone of the package: because the contract is structural rather than nominal, none of the concrete drivers subclass anything — they simply implement async execute(task, files, is_fix) -> (bool, str), and CodeDirector can hold and swap them interchangeably. This is why the class diagrams show dashed realization arrows rather than a solid inheritance tree; the design intentionally trades an inheritance hierarchy for a single protocol boundary.

driver_factory.py — provider dispatch

get_driver(volk, workspace, env) is the package's single public entry point for obtaining a driver. It resolves the volk's provider through llm.config.get_volk_provider, then dispatches: claude/anthropic and any unknown provider → ClaudeAPIDriver; claude_code → ClaudeCodeDriver; deepseek → DeepSeekCoderDriver; aider → AiderDriver; qwen/ollama → QwenDriver; and the *_browser providers → BrowserDriver (mapped to a browser_providers key via _BROWSER_PROVIDER_MAP). Historically this dispatch lived, duplicated and drifting, inside the spike's swarm.py, codedirector.py, and junior.py; the factory consolidates it so a new backend needs exactly one registration point.

CLI subprocess drivers (aider, claude_code, claude_api, qwen)

The four CLI drivers share one shape: find the binary (_find_aider / _find_claude / _find_qwen, honouring an environment override before falling back to shutil.which and well-known paths), read a timeout from the environment (ORCHESTRATA_*_TIMEOUT_SECONDS), then run the tool as an async subprocess. They differ in how output becomes files. AiderDriver invokes Aider non-interactively and lets it edit the tree, optionally passing --file for named targets and --model from AIDER_MODEL. ClaudeCodeDriver runs claude --print --dangerously-skip-permissions, streaming stdout line-by-line and enforcing the timeout with asyncio.wait_for rather than a watchdog thread. ClaudeAPIDriver pipes the prompt over stdin — dodging E7 "Argument list too long" on 40-file prompts — and, since the agentic CLI writes files itself, scans the workspace afterwards (_scan_source_files, skipping .venv/__pycache__/.git/node_modules/etc.) to report what was created. QwenDriver follows the same stdin-pipe-then-scan pattern with qwen --yolo --max-session-turns 50 -o text -p -, running qwen init first if no QWEN.md exists.

deepseek_coder_driver.py — direct-from-LLM generation

DeepSeekCoderDriver is the one driver that needs no external tool: it calls the LLM through orchestrata_ultimate.llm.client.call_llm (as volk J:Junior, with a system prompt demanding complete FILE: blocks) and turns the raw text response into files itself. The pipeline runs through a chain of helpers — _extract_and_write_files and _extract_code_block parse FILE: blocks out of the response, _normalize_path sanitizes target paths against known roots (src/tests/design/docs), _validate_written_files syntax-checks and ruff-lints what was written, and _autofix_lint clears F401 unused imports automatically. When extraction comes back empty the driver retries with a stricter prompt (_retry_on_empty); when a file the design expected is missing it retries again to recover it (_recover_missing_files / _find_missing_files) — rather than reporting a hollow success.

browser_driver.py + browser_providers.py — free-tier web UIs

BrowserDriver is the last-resort backend: when a volk is configured for a free-tier chat UI, it launches Playwright headless Chromium, navigates to the provider page, types the prompt into the input selector, waits for the completion indicator, and scrapes code blocks out of the response. To behave as a well-mannered client it enforces the provider's rate_limit_seconds between requests (_enforce_rate_limit) and treats a session older than max_session_minutes as expired (_is_session_expired). Login state is persisted to .browser_state/<provider>_state.json (_save_storage_state) and reloaded via new_context(storage_state=...) on the next launch, so an authenticated session survives across runs.

When a request fails, execute runs a bounded retry loop (_MAX_RECOVERY_ATTEMPTS) with escalating _recover_session levels: level 1 re-navigates the existing page back to the base URL, and level 2 closes the browser, waits, and relaunches it (which reloads the saved login state). An expired session skips straight to a level-2 restart before the first attempt. All the provider-specific knobs — base_url, login_url, the input/submit/response/completion/code-block CSS selectors, and the three limits — live in the frozen BrowserProviderConfig dataclass, with concrete entries for qwen_chat, deepseek_chat, and kimi registered in BROWSER_PROVIDERS and looked up by get_provider_config. The module-level should_inject(provider) helper reports whether a provider string maps to this driver.

claude_settings.py — governance settings for Claude Code

claude_settings.py produces the settings file that wires the SPEC-025 governance gate into a Claude Code run. write_governance_settings writes .claude/orchestrata-settings.json inside the workspace and returns its path; the settings register a PreToolUse hook that gates every Claude Code tool call through the real tool interceptor in core.adapters.claude. The module only produces the file; ClaudeCodeDriver hands the path to the CLI.

extraction.py — shared response parsing

extraction.py holds the two parsers the browser and API paths both need: extract_file_blocks(response) splits on FILE: markers and returns (filename, content) tuples, and extract_markdown_code_blocks(response) is the fallback that pulls fenced code blocks (with optional language tags) when no FILE: headers are present. Both are pure functions traced to SPEC-025, keeping the "text → files" logic in one place instead of re-implemented per driver.


Grounded in src/orchestrata_ultimate/drivers/ (SPEC-025). Traceability via @traces decorators from core.traceability.

Class map (generated)

Generated from the live source AST by scripts/gen-module-class-diagrams.py — do not hand-edit. Abstract classes have a green border; members listed are those defined on the class itself (inherited members excluded). Solid empty-triangle arrows = in-package inheritance; dashed arrows into dashed boxes = external/stdlib/cross-package bases; filled diamond = composition (constructed in __init__); open diamond = aggregation (annotated/injected reference); diamond edge labels name the owning attribute.

drivers class map