Orchestrata Ultimate / Specs / 025-code-generation-drivers
025-code-generation-drivers: Code Generation Drivers
The Code Generation Drivers package (orchestrata-drivers) provides a uniform async interface over multiple external code generation tools and LLM providers. A single Protocol (AsyncCodeDriver) is implemented by seven concrete drivers covering Claude Code CLI, Anthropic API (via Claude Code stdin pipe), Aider CLI, DeepSeek API, Qwen CLI, and three browser-based free-tier LLM web UIs (Qwen Chat, DeepSeek Chat, Kimi). A factory function (get_driver) selects the correct driver from a...
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/025-code-generation-drivers/ |
| Status line in the spec | Implemented |
| Requirement IDs defined | 102 |
With a @traces citation in src/ |
82 (80.4%) |
With a @traces citation in tests/ |
3 (2.9%) |
| Tracked files | 6 |
| RTM page | 025-code-generation-drivers |
Reading this spec
The Code Generation Drivers package gives a uniform async interface over several external code generation tools and LLM providers. One protocol, AsyncCodeDriver, is implemented by seven concrete drivers covering the Claude Code CLI, the Anthropic API, Aider, the DeepSeek API, the Qwen CLI and three browser-based free-tier LLM web UIs. It exists because the CodeDirector's swarm coordinator needs one interface to dispatch code generation to any backend, provider-selection logic was duplicated across callers, and browser automation complexity must stay out of business logic.
The scope is four user stories (unified code generation interface, browser-based free-tier LLM access, code artifact extraction, provider configuration routing) and twelve functional requirements, from the protocol and driver factory routing through binary detection, subprocess timeout enforcement, workspace file scanning, DeepSeek integration, browser session persistence, three-tier browser recovery, rate limiting, code extraction utilities, the unknown provider fallback and runtime protocol checkability. Requirement IDs use the REQ-025- prefix.
The key decisions are that AsyncCodeDriver defines execute(task, files, is_fix) returning a success flag and text; get_driver(volk, workspace) reads the Volk's provider from the LLM configuration and instantiates the right driver, so routing is configuration-driven; CLI drivers wrap external binaries with enforced timeouts; the browser driver uses Playwright headless Chromium with per-provider configuration; and browser sessions persist to JSON state files and recover through navigate, restart and re-authenticate escalation.
In the code, the package is orchestrata_ultimate/drivers. The largest tracers are orchestrata_ultimate/drivers/browser_driver, orchestrata_ultimate/drivers/claude_code_driver, orchestrata_ultimate/drivers/driver_factory, orchestrata_ultimate/drivers/claude_api_driver and orchestrata_ultimate/drivers/extraction. The per-requirement view is on the RTM page.
The spec's status line reads Implemented. It tracks 6 files and defines 102 requirement IDs. The full text follows below.
Measured coverage is 82 of 102 requirements cited by at least one @traces in src/ (80.4%), spread over 11 modules; the module citing the most of them is orchestrata_ultimate/drivers/browser_driver (22 requirements). In tests/, 3 requirements are cited by a test-side @traces.
Coverage gaps: 20 requirements have no citing symbol. The largest untraced groups are REQ-025-* (20). Untraced means no @traces names the ID; the behaviour may still exist in code that was never annotated. Test-side citations are thinner than code-side ones (3 against 82).
Requirements
Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.
| ID | Done | Requirement | Traced by |
|---|---|---|---|
REQ-025-001 |
no | AsyncCodeDriver defined as runtime_checkable Protocol in base.py |
1 |
REQ-025-002 |
no | Protocol decorated with @traces(spec="025") for traceability |
1 |
REQ-025-003 |
no | execute(task, files, is_fix) method signature enforced on all drivers |
2 |
REQ-025-004 |
no | Return type is tuple[bool, str] — (success, output_text) |
2 |
REQ-025-005 |
no | files parameter accepts list[str] | None (optional, None by default) |
1 |
REQ-025-006 |
no | is_fix boolean parameter signals repair vs new generation invocation |
1 |
REQ-025-007 |
no | get_driver(volk, workspace) reads provider via get_volk_provider(volk) |
1 |
REQ-025-008 |
no | Default volk is J:Junior when no volk argument provided |
1 |
REQ-025-009 |
no | Workspace defaults to ORCHESTRATA_WORKSPACE env var if not provided |
1 |
REQ-025-010 |
no | Workspace falls back to /home/mohamed/workspace if env var unset |
1 |
REQ-025-011 |
no | Provider claude or anthropic routes to ClaudeAPIDriver |
2 |
REQ-025-012 |
no | Provider deepseek routes to DeepSeekCoderDriver |
2 |
REQ-025-013 |
no | Provider aider routes to AiderDriver |
2 |
REQ-025-014 |
no | Provider qwen or ollama routes to QwenDriver |
2 |
REQ-025-015 |
no | Provider qwen_browser routes to BrowserDriver(provider="qwen_chat") |
2 |
REQ-025-016 |
no | Provider deepseek_browser routes to BrowserDriver(provider="deepseek_chat") |
2 |
REQ-025-017 |
no | Provider kimi_browser routes to BrowserDriver(provider="kimi") |
2 |
REQ-025-018 |
no | Unrecognised provider logs a warning and falls back to ClaudeAPIDriver |
1 |
REQ-025-019 |
no | Binary located via shutil.which("claude") then fallback paths |
2 |
REQ-025-020 |
no | Fallback paths checked: ~/.local/bin/claude, /usr/bin/claude, /usr/local/bin/claude |
2 |
REQ-025-021 |
no | Command: claude --print --dangerously-skip-permissions <task> |
2 |
REQ-025-022 |
no | Output streamed line-by-line from stdout+stderr combined | 1 |
REQ-025-023 |
no | Timeout of 900 seconds enforced via asyncio.wait_for |
2 |
REQ-025-024 |
no | Non-zero exit code returns (False, output) |
1 |
REQ-025-025 |
no | Binary not found returns (False, "Claude Code CLI not found") |
1 |
REQ-025-026 |
no | Timeout returns (False, "Timed out after 900s") |
1 |
REQ-025-027 |
no | Task prompt delivered via stdin pipe (avoids E7 arg-list-too-long) | 2 |
REQ-025-028 |
no | Runs claude init --yes if CLAUDE.md absent from workspace |
1 |
REQ-025-029 |
no | Init skipped silently if CLAUDE.md already present | 1 |
REQ-025-030 |
no | Post-execution workspace scan enumerates created source files | 1 |
REQ-025-031 |
no | Scan covers extensions: .py, .toml, .cfg, .ini, .yaml, .yml |
none |
REQ-025-032 |
no | Scan skips directories: .venv, __pycache__, .git, node_modules, .claude, design |
none |
REQ-025-033 |
no | Returns (False, "No files generated") when scan finds no source files |
1 |
REQ-025-034 |
no | Timeout of 1200 seconds enforced via asyncio.wait_for |
1 |
REQ-025-035 |
no | Binary located via shutil.which("aider") then fallback paths |
2 |
REQ-025-036 |
no | Command: aider --message <task> --yes-always --no-auto-commits |
2 |
REQ-025-037 |
no | Each path in files appended as separate --file <path> argument |
2 |
REQ-025-038 |
no | Timeout of 300 seconds enforced | 3 |
REQ-025-039 |
no | Non-zero exit returns (False, output) |
2 |
REQ-025-040 |
no | Binary not found returns (False, "Aider CLI not found") |
2 |
REQ-025-041 |
no | API key read from DEEPSEEK_API_KEY environment variable |
none |
REQ-025-042 |
no | Missing API key returns (False, error_message) immediately |
none |
REQ-025-043 |
no | Uses model=deepseek-coder for code generation requests |
none |
REQ-025-044 |
no | Constructs system + user message pair for the API call | 1 |
REQ-025-045 |
no | API errors return (False, error_message) with error detail |
1 |
REQ-025-046 |
no | Binary located via shutil.which("qwen") then fallback paths |
2 |
REQ-025-047 |
no | Task passed as positional argument to the Qwen CLI | none |
REQ-025-048 |
no | Timeout of 600 seconds enforced | 2 |
REQ-025-049 |
no | Non-zero exit returns (False, output) |
none |
REQ-025-050 |
no | Binary not found returns (False, "Qwen CLI not found") |
1 |
REQ-025-051 |
no | Uses playwright.async_api with headless Chromium |
3 |
REQ-025-052 |
no | headless parameter controls visible vs headless mode (default: True) |
1 |
REQ-025-053 |
no | Browser state persisted to .browser_state/<provider>_state.json under workspace |
1 |
REQ-025-054 |
no | Existing state file loaded via storage_state on launch |
2 |
REQ-025-055 |
no | close() saves storage state before closing browser |
2 |
REQ-025-056 |
no | Rate limit enforced: waits rate_limit_seconds minimum between requests |
1 |
REQ-025-057 |
no | Session expiry checked against max_session_minutes on each execute() |
1 |
REQ-025-058 |
no | Expired session triggers level-2 recovery (restart) before execute | 1 |
REQ-025-059 |
no | _session_start timestamp reset on each browser launch |
2 |
REQ-025-060 |
no | Max 3 recovery attempts per execute() call | 1 |
REQ-025-061 |
no | Level 1 recovery: navigate page back to base_url |
1 |
REQ-025-062 |
no | Level 2 recovery: close browser, wait 3 seconds, relaunch | 1 |
REQ-025-063 |
no | Level 1 failure escalates to level 2 automatically | 1 |
REQ-025-064 |
no | All attempts exhausted returns (False, "All recovery attempts exhausted") |
1 |
REQ-025-065 |
no | FILE: format instructions appended to every prompt | 1 |
REQ-025-066 |
no | File context included in prompt for non-fix tasks when files provided | 1 |
REQ-025-067 |
no | File context truncated at 5000 characters per file | 1 |
REQ-025-068 |
no | Files not found in workspace silently skipped in context | 1 |
REQ-025-069 |
no | extract_file_blocks called first on response text |
1 |
REQ-025-070 |
no | Falls back to extract_markdown_code_blocks when no FILE: blocks found |
1 |
REQ-025-071 |
no | No code blocks found returns (False, "No code blocks found in response") |
1 |
REQ-025-072 |
no | Extracted FILE: blocks written to workspace disk with directory creation | 1 |
REQ-025-073 |
no | BrowserProviderConfig is a frozen dataclass |
1 |
REQ-025-074 |
no | Config contains: name, base_url, login_url, input_selector, submit_selector, response_selector, completion_indicator, code_block_selector, rate_limit_seconds, max_session_minutes, timeout_seconds | 1 |
REQ-025-075 |
no | BROWSER_PROVIDERS dict registers qwen_chat, deepseek_chat, kimi |
none |
REQ-025-076 |
no | get_provider_config(provider) raises ValueError for unrecognised key |
1 |
REQ-025-077 |
no | qwen_chat: rate_limit=5s, max_session=60min, timeout=120s | none |
REQ-025-078 |
no | deepseek_chat: rate_limit=10s, max_session=30min, timeout=180s | none |
REQ-025-079 |
no | kimi: rate_limit=8s, max_session=45min, timeout=150s | none |
REQ-025-080 |
no | extract_file_blocks splits response on ^FILE:\s* (multiline regex) |
1 |
REQ-025-081 |
no | Filename extracted from first line after FILE: marker | 1 |
REQ-025-082 |
no | Fenced code block following filename extracted via state machine | 2 |
REQ-025-083 |
no | Blocks with empty filenames or empty code silently skipped | 1 |
REQ-025-084 |
no | extract_markdown_code_blocks uses DOTALL regex for multi-line blocks |
1 |
REQ-025-085 |
no | Language tag captured in markdown extraction (empty string if absent) | 1 |
REQ-025-086 |
no | Blocks with only whitespace content skipped | 1 |
REQ-025-087 |
no | Package name is orchestrata-drivers, namespace orchestrata.drivers |
none |
REQ-025-088 |
no | __init__.py exports: AsyncCodeDriver, BrowserDriver, BrowserProviderConfig, BROWSER_PROVIDERS, get_driver, extract_file_blocks, extract_markdown_code_blocks |
none |
REQ-025-089 |
no | All public classes and functions carry @traces decorator |
none |
REQ-025-090 |
no | playwright listed as optional dependency under [browser] extra |
none |
REQ-025-091 |
no | ClaudeCodeDriver accepts an optional volk alias and resolves it to a --model value via get_volk_model |
1 |
REQ-025-092 |
no | ClaudeCodeDriver appends the shared team persona via --append-system-prompt when a volk alias is supplied |
1 |
REQ-025-093 |
no | ClaudeCodeDriver emits --output-format json on every invocation and parses the resulting JSON envelope |
2 |
REQ-025-094 |
no | ClaudeCodeDriver emits --resume <last_session_id> on is_fix when a previous session id was captured from the JSON envelope |
2 |
REQ-025-095 |
no | ClaudeCodeDriver emits --settings <path> when ORCHESTRATA_CLAUDE_SETTINGS or auto-generated governance settings are present |
1 |
REQ-025-096 |
no | ClaudeCodeDriver.__init__ auto-generates .claude/orchestrata-settings.json when ORCHESTRATA_CLAUDE_GOVERNANCE=1 and no explicit settings path is set |
2 |
REQ-025-097 |
no | write_governance_settings writes a stable, sorted .claude/orchestrata-settings.json wiring a PreToolUse hook to scripts/claude_governance_gate.py |
2 |
REQ-025-098 |
no | claude_governance_gate reads a JSON payload from stdin and extracts tool_name, tool_input, and session_id |
none |
REQ-025-099 |
no | claude_governance_gate delegates the allow/deny decision to ToolInterceptor.intercept() |
none |
REQ-025-100 |
no | claude_governance_gate emits the Claude Code deny-decision envelope when the interceptor returns BLOCK |
none |
REQ-025-101 |
no | claude_governance_gate fails open (exit 0, allow) on malformed stdin or interceptor construction/call failure |
none |
REQ-025-102 |
no | claude_governance_gate appends one JSONL audit line per decision when ORCHESTRATA_CLAUDE_GATE_LOG is set |
none |
Code that traces this spec
Checklists
checklists/constitutional.md
Code Generation Drivers Constitutional Checklist
Security and Access Control
- [ ] CONST-025-001: External binary execution must validate binary path before invocation; no user-supplied binary paths accepted
- [ ] CONST-025-002: Task prompts must not be logged at INFO level or above to prevent secret leakage; DEBUG only
- [ ] CONST-025-003:
--dangerously-skip-permissionsflag on Claude CLI is intentional and scoped to Orchestrata workspace only; workspace isolation must be maintained - [ ] CONST-025-004: Browser state files (login cookies) must be stored under the workspace and excluded from version control
- [ ] CONST-025-005:
DEEPSEEK_API_KEYmust never appear in log output at any level
Subprocess Safety
- [ ] CONST-025-006: All subprocess executions must use list-form
create_subprocess_exec(not shell=True) to prevent shell injection - [ ] CONST-025-007: All subprocess invocations must have a hard timeout via
asyncio.wait_forto prevent hanging - [ ] CONST-025-008: Subprocess stdout and stderr must be consumed (not left open) to prevent deadlock on buffer fill
- [ ] CONST-025-009: Process return codes must be checked; non-zero must not be silently treated as success
Filesystem Safety
- [ ] CONST-025-010: Drivers must only write files within the provided workspace directory; path traversal outside workspace is prohibited
- [ ] CONST-025-011: ClaudeAPIDriver workspace scan must not follow symlinks outside the workspace root
- [ ] CONST-025-012: FILE: block extraction must sanitise filenames before writing; paths starting with
/or containing..must be rejected
Browser Automation Safety
- [ ] CONST-025-013: BrowserDriver must not submit prompts containing API keys, passwords, or other secrets to third-party web UIs
- [ ] CONST-025-014: Browser session storage state files must have restricted file permissions (owner read/write only)
- [ ] CONST-025-015: 3-tier recovery must not attempt re-authentication automatically without explicit configuration; default recovery is navigate + restart only
checklists/requirements.md
The requirement list above comes from this file; 0 of 102 checkbox items are ticked.
checklists/testing.md
Code Generation Drivers Testing Checklist
AsyncCodeDriver Protocol
- [ ] TEST-025-001: Verify
AsyncCodeDriverisruntime_checkable—isinstance(driver, AsyncCodeDriver)returns True for all concrete drivers - [ ] TEST-025-002: Verify Protocol method signature:
execute(task, files=None, is_fix=False) - [ ] TEST-025-003: Verify return type annotation is
tuple[bool, str] - [ ] TEST-025-004: Verify a class missing
executefails isinstance check against AsyncCodeDriver
get_driver Factory
- [ ] TEST-025-005:
get_driver("J:Junior")with provider=claude returnsClaudeAPIDriverinstance - [ ] TEST-025-006:
get_driverwith provider=anthropic returnsClaudeAPIDriverinstance - [ ] TEST-025-007:
get_driverwith provider=deepseek returnsDeepSeekCoderDriverinstance - [ ] TEST-025-008:
get_driverwith provider=aider returnsAiderDriverinstance - [ ] TEST-025-009:
get_driverwith provider=qwen returnsQwenDriverinstance - [ ] TEST-025-010:
get_driverwith provider=ollama returnsQwenDriverinstance - [ ] TEST-025-011:
get_driverwith provider=qwen_browser returnsBrowserDriverwith provider_name=qwen_chat - [ ] TEST-025-012:
get_driverwith provider=deepseek_browser returnsBrowserDriverwith provider_name=deepseek_chat - [ ] TEST-025-013:
get_driverwith provider=kimi_browser returnsBrowserDriverwith provider_name=kimi - [ ] TEST-025-014:
get_driverwith unknown provider logs warning and returnsClaudeAPIDriverfallback - [ ] TEST-025-015:
get_driverworkspace parameter is passed through to returned driver instance - [ ] TEST-025-016:
get_driverwithout workspace usesORCHESTRATA_WORKSPACEenv var
ClaudeCodeDriver
- [ ] TEST-025-017:
executereturns(False, "Claude Code CLI not found")when claude binary absent - [ ] TEST-025-018:
executereturns(False, output)when subprocess exits non-zero - [ ] TEST-025-019:
executereturns(False, "Timed out after 900s")on asyncio.TimeoutError - [ ] TEST-025-020: Workspace directory is created if it does not exist on init
- [ ] TEST-025-021: Command constructed as list (not shell string):
[claude_bin, "--print", "--dangerously-skip-permissions", task] - [ ] TEST-025-022: Output lines collected correctly from streamed subprocess stdout
ClaudeAPIDriver
- [ ] TEST-025-023: Task delivered via stdin pipe (stdin written, drained, closed)
- [ ] TEST-025-024:
executereturns(False, "No files generated")when workspace scan finds no source files - [ ] TEST-025-025: Workspace scan returns .py, .toml, .cfg, .ini, .yaml, .yml files
- [ ] TEST-025-026: Workspace scan excludes .venv, pycache, .git, node_modules, .claude, design directories
- [ ] TEST-025-027:
_init_projectskipped when CLAUDE.md already present in workspace - [ ] TEST-025-028:
executereturns(False, msg)on asyncio.TimeoutError (1200s limit) - [ ] TEST-025-029: Scan result includes relative paths, sorted alphabetically
AiderDriver
- [ ] TEST-025-030:
executereturns(False, "Aider CLI not found")when aider binary absent - [ ] TEST-025-031: Files list generates correct
--fileflags in subprocess command - [ ] TEST-025-032:
executewith empty files list runs without--filearguments - [ ] TEST-025-033: Timeout of 300 seconds enforced (not 900 or 1200)
- [ ] TEST-025-034: Non-zero exit from aider returns
(False, output)
DeepSeekCoderDriver
- [ ] TEST-025-035: Missing
DEEPSEEK_API_KEYreturns(False, error_message)without network call - [ ] TEST-025-036: API error response returns
(False, error_message)with error detail - [ ] TEST-025-037: Successful API response returns
(True, completion_text) - [ ] TEST-025-038: Model
deepseek-coderused in API request payload
QwenDriver
- [ ] TEST-025-039:
executereturns(False, "Qwen CLI not found")when qwen binary absent - [ ] TEST-025-040: Timeout of 600 seconds enforced
- [ ] TEST-025-041: Task passed as positional argument to qwen CLI command
BrowserDriver — Init and Launch
- [ ] TEST-025-042:
BrowserDriverraisesImportErrorwhen playwright not installed and execute called - [ ] TEST-025-043:
_ensure_browseris idempotent — second call does not relaunch - [ ] TEST-025-044: Workspace
.browser_statedirectory created on init - [ ] TEST-025-045:
BrowserProviderConfigloaded fromBROWSER_PROVIDERSon init - [ ] TEST-025-046: Unknown provider raises
ValueErrorfromget_provider_config
BrowserDriver — Rate Limiting and Session
- [ ] TEST-025-047: Rate limit wait enforced between consecutive execute() calls
- [ ] TEST-025-048: No wait applied on first request (
_last_request == 0.0) - [ ] TEST-025-049: Session expiry detected correctly after
max_session_minuteselapsed - [ ] TEST-025-050: Fresh session (
_session_start == 0.0) is never considered expired
BrowserDriver — Prompt Construction
- [ ] TEST-025-051: FILE: format instructions appended to all prompts
- [ ] TEST-025-052: File context included when is_fix=False and files provided
- [ ] TEST-025-053: File context excluded when is_fix=True (repair prompt)
- [ ] TEST-025-054: File content truncated at 5000 characters per file
- [ ] TEST-025-055: Missing files silently skipped in context building
- [ ] TEST-025-056: Unreadable files (OSError, UnicodeDecodeError) silently skipped
BrowserDriver — Recovery
- [ ] TEST-025-057: Level 1 recovery navigates to
base_urlwithout restarting browser - [ ] TEST-025-058: Level 2 recovery closes browser, waits 3 seconds, relaunches
- [ ] TEST-025-059: Level 1 failure escalates to level 2
- [ ] TEST-025-060: All 3 attempts exhausted returns
(False, "All recovery attempts exhausted") - [ ] TEST-025-061: execute() returns
(False, "Browser init failed: <exc>")on launch exception
BrowserDriver — Response Processing
- [ ] TEST-025-062: FILE: blocks extracted and written to workspace correctly
- [ ] TEST-025-063: Fallback to markdown extraction when no FILE: blocks present
- [ ] TEST-025-064: Returns
(False, "No code blocks found in response")when neither extractor yields results - [ ] TEST-025-065: Written file directories created recursively if absent
- [ ] TEST-025-066:
close()saves storage state to JSON path before closing browser
BrowserProviderConfig
- [ ] TEST-025-067: All three providers registered: qwen_chat, deepseek_chat, kimi
- [ ] TEST-025-068: qwen_chat config: rate_limit=5, max_session=60, timeout=120
- [ ] TEST-025-069: deepseek_chat config: rate_limit=10, max_session=30, timeout=180
- [ ] TEST-025-070: kimi config: rate_limit=8, max_session=45, timeout=150
- [ ] TEST-025-071:
BrowserProviderConfigis immutable (frozen dataclass — AttributeError on mutation attempt) - [ ] TEST-025-072:
get_provider_configraisesValueErrorwith message listing available providers
extraction.py — extract_file_blocks
- [ ] TEST-025-073: Single FILE: block with Python code extracted correctly
- [ ] TEST-025-074: Multiple FILE: blocks in one response all extracted
- [ ] TEST-025-075: Filename with subdirectory path (
src/foo/bar.py) extracted correctly - [ ] TEST-025-076: Block with empty filename returns empty list
- [ ] TEST-025-077: Block with no fenced code after filename returns empty list for that block
- [ ] TEST-025-078: Response with no FILE: markers returns empty list
extraction.py — extract_markdown_code_blocks
- [ ] TEST-025-079: Python fenced block extracted with lang="python"
- [ ] TEST-025-080: Block without language tag extracted with lang=""
- [ ] TEST-025-081: Multiple blocks in one response all extracted
- [ ] TEST-025-082: Block containing only whitespace excluded from results
- [ ] TEST-025-083: Multi-line code content preserved correctly (DOTALL behaviour)
- [ ] TEST-025-084: Empty response returns empty list
Integration Tests
- [ ] TEST-025-085:
get_driverreturns instance satisfyingisinstance(driver, AsyncCodeDriver)for all providers - [ ] TEST-025-086: Full extraction pipeline: raw LLM response with FILE: blocks → written files in temp workspace
- [ ] TEST-025-087:
should_inject(provider)returns True for*_browserproviders only - [ ] TEST-025-088:
__init__.pyexports all public API symbols without ImportError
Other tracked files
specs/025-code-generation-drivers/README.mdspecs/025-code-generation-drivers/compliance-audit.md
Full spec text
Verbatim from specs/025-code-generation-drivers/spec.md; headings are pushed down two levels.
SPEC-025: Code Generation Drivers
Metadata
- Spec ID: 025-code-generation-drivers
- Title: Code Generation Drivers
- Version: 1.0.0
- Author: Mohamed
- Status: Implemented
- Created: 2026-04-04
- Last Updated: 2026-04-04
- Priority: P1
- Components: AsyncCodeDriver, ClaudeCodeDriver, ClaudeAPIDriver, AiderDriver, DeepSeekCoderDriver, QwenDriver, BrowserDriver, BrowserProviderConfig, get_driver, extract_file_blocks, extract_markdown_code_blocks
- Dependencies: Python 3.14t, orchestrata-core, orchestrata-llm, playwright (optional)
Overview
What
The Code Generation Drivers package (orchestrata-drivers) provides a uniform async interface over multiple external code generation tools and LLM providers. A single Protocol (AsyncCodeDriver) is implemented by seven concrete drivers covering Claude Code CLI, Anthropic API (via Claude Code stdin pipe), Aider CLI, DeepSeek API, Qwen CLI, and three browser-based free-tier LLM web UIs (Qwen Chat, DeepSeek Chat, Kimi). A factory function (get_driver) selects the correct driver from a volk's configured LLM provider. Shared extraction utilities parse FILE: blocks and markdown code blocks from raw LLM output.
Why
- CodeDirector's swarm coordinator (SPEC-020, SPEC-029) needs a single interface to dispatch code generation tasks to any backend without conditional logic spread across multiple volks
- Multiple callers (swarm.py, codedirector.py, junior.py) duplicated provider-selection logic that needed consolidating
- Browser-based free-tier LLMs require Playwright automation with session management, rate limiting, and 3-tier recovery — complexity that must be isolated from business logic
- Extraction of code artifacts from LLM responses is shared across browser and API drivers and must not be duplicated
- Provider routing must be configuration-driven (via orchestrata-llm), not hardcoded in calling code
How
AsyncCodeDriverProtocol (ABC, runtime-checkable) definesexecute(task, files, is_fix) -> (bool, str)get_driver(volk, workspace)reads the volk's provider fromorchestrata-llmconfig and instantiates the correct driver- CLI drivers (
ClaudeCodeDriver,ClaudeAPIDriver,AiderDriver,QwenDriver) wrap external binaries viaasyncio.create_subprocess_execwithasyncio.wait_fortimeout enforcement ClaudeAPIDriveradditionally scans the workspace after completion to report created filesDeepSeekCoderDrivercalls the DeepSeek HTTP API directlyBrowserDriveruses Playwright headless Chromium with per-providerBrowserProviderConfig(CSS selectors, URLs, timeouts, rate limits)- Browser sessions are persisted to JSON state files and recovered via 3-tier escalation (navigate → restart → re-auth)
extraction.pyprovides two regex-based parsers for FILE: blocks and markdown code blocks
User Scenarios
US-001: Unified Code Generation Interface
- As a CodeDirector or Junior volk developer
- I want to dispatch code generation tasks through a single async interface
- So that I don't need conditional logic for each provider in my swarm coordinator
Acceptance Criteria:
- Callers invoke
driver.execute(task, files, is_fix)without knowing the provider - Drivers handle all provider-specific complexity internally
- Returns
(bool, str)tuple with success flag and output
US-002: Browser-Based Free-Tier LLM Access
- As a developer needing cost-effective code generation
- I want to use free-tier web UIs (Qwen Chat, DeepSeek Chat, Kimi) via Playwright
- So that I can generate code without API costs while handling session recovery
Acceptance Criteria:
- BrowserDriver automates login, prompt submission, and response extraction
- 3-tier recovery handles session expiry gracefully
- Rate limiting prevents account throttling
US-003: Code Artifact Extraction
- As a caller processing LLM responses
- I want to parse FILE: blocks and markdown code blocks from raw output
- So that I can write generated code to disk without duplication
Acceptance Criteria:
extract_file_blocks()parses FILE: format with filename and contentextract_markdown_code_blocks()extracts fenced code blocks- Returns list of
(filename, content)tuples
US-004: Provider Configuration Routing
- As a volk configuration manager
- I want to specify the LLM provider in orchestrata-llm config
- So that get_driver auto-selects the correct driver without hardcoding
Acceptance Criteria:
- Factory reads provider from
get_volk_provider(volk) - Supports 7+ provider strings mapping to specific drivers
- Falls back to ClaudeAPIDriver with warning for unknown providers
What
The Code Generation Drivers package (orchestrata-drivers) provides a uniform async interface over multiple external code generation tools and LLM providers. A single Protocol (AsyncCodeDriver) is implemented by seven concrete drivers covering Claude Code CLI, Anthropic API (via Claude Code stdin pipe), Aider CLI, DeepSeek API, Qwen CLI, and three browser-based free-tier LLM web UIs (Qwen Chat, DeepSeek Chat, Kimi). A factory function (get_driver) selects the correct driver from a volk's configured LLM provider. Shared extraction utilities parse FILE: blocks and markdown code blocks from raw LLM output.
Why
- CodeDirector's swarm coordinator (SPEC-020, SPEC-029) needs a single interface to dispatch code generation tasks to any backend without conditional logic spread across multiple volks
- Multiple callers (swarm.py, codedirector.py, junior.py) duplicated provider-selection logic that needed consolidating
- Browser-based free-tier LLMs require Playwright automation with session management, rate limiting, and 3-tier recovery — complexity that must be isolated from business logic
- Extraction of code artifacts from LLM responses is shared across browser and API drivers and must not be duplicated
- Provider routing must be configuration-driven (via orchestrata-llm), not hardcoded in calling code
How
AsyncCodeDriverProtocol (ABC, runtime-checkable) definesexecute(task, files, is_fix) -> (bool, str)get_driver(volk, workspace)reads the volk's provider fromorchestrata-llmconfig and instantiates the correct driver- CLI drivers (
ClaudeCodeDriver,ClaudeAPIDriver,AiderDriver,QwenDriver) wrap external binaries viaasyncio.create_subprocess_execwithasyncio.wait_fortimeout enforcement ClaudeAPIDriveradditionally scans the workspace after completion to report created filesDeepSeekCoderDrivercalls the DeepSeek HTTP API directlyBrowserDriveruses Playwright headless Chromium with per-providerBrowserProviderConfig(CSS selectors, URLs, timeouts, rate limits)- Browser sessions are persisted to JSON state files and recovered via 3-tier escalation (navigate → restart → re-auth)
extraction.pyprovides two regex-based parsers for FILE: blocks and markdown code blocks
Data Models
AsyncCodeDriverResult
{
"type": "object",
"properties": {
"success": {
"type": "boolean",
"description": "True if driver executed without errors"
},
"output": {
"type": "string",
"description": "Raw output from the driver or error message"
}
},
"required": ["success", "output"]
}
BrowserProviderConfig
{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Human-readable provider label"
},
"base_url": {
"type": "string",
"format": "uri",
"description": "Chat page URL"
},
"login_url": {
"type": "string",
"format": "uri",
"description": "Authentication page URL"
},
"input_selector": {
"type": "string",
"description": "CSS selector for prompt textarea"
},
"submit_selector": {
"type": "string",
"description": "CSS selector for send button"
},
"response_selector": {
"type": "string",
"description": "CSS selector for response container"
},
"completion_indicator": {
"type": "string",
"description": "CSS selector appearing on generation complete"
},
"code_block_selector": {
"type": "string",
"description": "CSS selector for code blocks"
},
"rate_limit_seconds": {
"type": "integer",
"minimum": 1,
"description": "Minimum seconds between requests"
},
"max_session_minutes": {
"type": "integer",
"minimum": 1,
"description": "Max session duration before re-auth"
},
"timeout_seconds": {
"type": "integer",
"minimum": 10,
"description": "Per-request timeout"
}
},
"required": ["name", "base_url", "login_url", "input_selector", "submit_selector", "response_selector", "completion_indicator", "code_block_selector", "rate_limit_seconds", "max_session_minutes", "timeout_seconds"]
}
CodeGenerationTask
{
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "The full coding prompt to submit"
},
"files": {
"type": ["array", "null"],
"items": {"type": "string"},
"description": "Optional list of relative file paths"
},
"is_fix": {
"type": "boolean",
"description": "True if prompt is a repair/fix invocation"
}
},
"required": ["task"]
}
ExtractedCodeBlock
{
"type": "object",
"properties": {
"filename": {
"type": ["string", "null"],
"description": "Filename for FILE: blocks, null for markdown"
},
"language": {
"type": "string",
"description": "Programming language identifier"
},
"content": {
"type": "string",
"description": "Extracted code content"
}
},
"required": ["content"]
}
Architecture
Component Diagram
CodeDirector / Junior Volk
|
v
get_driver(volk, workspace)
|
[provider config from orchestrata-llm]
|
+-- claude / anthropic --> ClaudeAPIDriver --> claude CLI (stdin pipe)
| \--> ClaudeCodeDriver --> claude --print
+-- deepseek --> DeepSeekCoderDriver --> DeepSeek HTTP API
+-- aider --> AiderDriver --> aider CLI
+-- qwen / ollama --> QwenDriver --> qwen CLI
+-- *_browser --> BrowserDriver --> Playwright --> LLM web UI
|
+-- qwen_browser --> qwen_chat (chat.qwen.ai)
+-- deepseek_browser --> deepseek_chat (chat.deepseek.com)
+-- kimi_browser --> kimi (kimi.moonshot.cn)
AsyncCodeDriver.execute(task, files, is_fix) -> (bool, str)
BrowserDriver._process_response(response)
|
+-- extract_file_blocks(response) -> [(filename, content)]
+-- extract_markdown_code_blocks(response) -> [(lang, content)]
File Inventory
| File | LOC | Responsibility |
|---|---|---|
base.py |
~30 | AsyncCodeDriver Protocol definition |
driver_factory.py |
~120 | get_driver factory; provider routing logic |
claude_code_driver.py |
~120 | Claude Code CLI via subprocess (--print mode) |
claude_api_driver.py |
~200 | Claude Code CLI via stdin pipe; workspace scan |
aider_driver.py |
~120 | Aider CLI subprocess driver |
deepseek_coder_driver.py |
~150 | DeepSeek HTTP API driver |
qwen_driver.py |
~120 | Qwen CLI subprocess driver |
browser_driver.py |
~380 | Playwright browser automation driver |
browser_providers.py |
~110 | BrowserProviderConfig dataclass + BROWSER_PROVIDERS registry |
extraction.py |
~90 | FILE: block and markdown code block extraction |
__init__.py |
~25 | Public API re-exports |
| Total | ~2,055 |
Terminology
| Term | Definition |
|---|---|
| AsyncCodeDriver | Runtime-checkable Protocol all drivers implement; single method: execute() |
| Driver | A concrete class that wraps a specific external code generation tool or API |
| ClaudeCodeDriver | CLI driver that runs claude --print --dangerously-skip-permissions as a subprocess |
| ClaudeAPIDriver | Claude Code CLI driver using stdin pipe; scans workspace for generated files |
| AiderDriver | CLI driver that runs aider --message --yes-always --no-auto-commits |
| DeepSeekCoderDriver | HTTP API driver calling the DeepSeek code generation endpoint |
| QwenDriver | CLI driver wrapping the Qwen local CLI tool |
| BrowserDriver | Playwright-based driver for free-tier LLM web UIs |
| BrowserProviderConfig | Frozen dataclass with CSS selectors, URLs, rate limits for a browser provider |
| BROWSER_PROVIDERS | Registry dict mapping provider keys to BrowserProviderConfig instances |
| get_driver | Factory function that reads volk provider config and returns the correct driver instance |
| Provider routing | Mapping of provider string → driver class (e.g. claude → ClaudeAPIDriver) |
| FILE: block | Structured output format: `FILE: path/to/file.py\n```lang\ncontent\n```` |
| 3-tier recovery | Browser session recovery: level 1 = page navigate, level 2 = browser restart |
| Storage state | Playwright JSON state file persisting login cookies for session reuse |
| Workspace scan | Post-generation walk of the workspace directory to enumerate created files |
Interfaces
AsyncCodeDriver Protocol
@runtime_checkable
@traces(spec="005", story="US-007", reqs=["REQ-005-US-007-001"])
class AsyncCodeDriver(Protocol):
async def execute(
self,
task: str,
files: list[str] | None = None,
is_fix: bool = False,
) -> tuple[bool, str]: ...
- task: The full coding prompt to submit to the external tool
- files: Optional list of relative file paths; interpretation is driver-specific
- is_fix: When True, the prompt is a repair/fix invocation (drivers may adjust behaviour)
- Returns:
(success: bool, output: str)— success is False if the tool exits non-zero, times out, or produces no artifacts
get_driver Factory
def get_driver(
volk: str = "J:Junior",
workspace: Optional[str] = None,
) -> AsyncCodeDriver: ...
- Reads
get_volk_provider(volk)fromorchestrata.llm.config - Falls back to
ClaudeAPIDriverfor unrecognised providers (with warning log) - Workspace defaults to
ORCHESTRATA_WORKSPACEenv var or/home/mohamed/workspace
Provider Routing Table
| Provider string | Driver |
|---|---|
claude, anthropic |
ClaudeAPIDriver |
deepseek |
DeepSeekCoderDriver |
aider |
AiderDriver |
qwen, ollama |
QwenDriver |
qwen_browser |
BrowserDriver(provider="qwen_chat") |
deepseek_browser |
BrowserDriver(provider="deepseek_chat") |
kimi_browser |
BrowserDriver(provider="kimi") |
| (unrecognised) | ClaudeAPIDriver (fallback, logged) |
Driver Specifications
ClaudeCodeDriver
- Locates the
claudebinary viashutil.whichand known fallback paths - Runs:
claude --print --dangerously-skip-permissions <task> - Output streamed line-by-line; collected and returned as string
- Returns
(False, output)on non-zero exit code or timeout - Timeout: 900 seconds enforced via
asyncio.wait_for
ClaudeAPIDriver
- Passes the task prompt via stdin pipe to avoid
E7 Argument list too long - Runs
claude init --yesifCLAUDE.mddoes not exist in the workspace - After completion, scans workspace for
.py,.toml,.cfg,.ini,.yaml,.ymlfiles - Skips directories:
.venv,__pycache__,.git,node_modules,.claude,design - Returns
(False, output)if no source files found after execution - Timeout: 1200 seconds enforced via
asyncio.wait_for
AiderDriver
- Locates the
aiderbinary viashutil.whichand known fallback paths - Runs:
aider --message <task> --yes-always --no-auto-commits [--file <f>...] - Passes each file in
filesas a separate--fileargument - Timeout: 300 seconds
DeepSeekCoderDriver
- Calls the DeepSeek HTTP API with
model=deepseek-coder - Uses
DEEPSEEK_API_KEYenvironment variable - Constructs a system + user message pair; returns completion text
- Returns
(False, error_message)on API errors or missing API key
QwenDriver
- Locates the
qwenbinary viashutil.whichand known fallback paths - Runs the Qwen CLI with the task as a positional argument
- Timeout: 600 seconds
BrowserDriver
- Supported providers:
qwen_chat,deepseek_chat,kimi - Uses
playwright.async_apiwith headless Chromium - Session state persisted to
.browser_state/<provider>_state.jsonunder workspace - Rate limiting: enforces
rate_limit_secondsminimum interval between requests - Session expiry: re-launches browser when
max_session_minutesexceeded - Prompt construction: appends FILE: format instructions; includes file context for non-fix prompts (truncated at 5000 chars per file)
- 3-tier recovery on attempt failure:
- Level 1:
page.goto(base_url)navigation - Level 2: close browser, wait 3s, relaunch
- Level 1:
- Max recovery attempts: 3
- Response processing:
extract_file_blocksfirst; falls back toextract_markdown_code_blocks - Writes extracted FILE: blocks to workspace disk
BrowserProviderConfig
Frozen dataclass fields:
| Field | Type | Purpose |
|---|---|---|
name |
str | Human-readable label |
base_url |
str | Chat page URL |
login_url |
str | Auth page URL |
input_selector |
str | CSS selector for prompt textarea |
submit_selector |
str | CSS selector for send button |
response_selector |
str | CSS selector for response container |
completion_indicator |
str | CSS selector appearing on generation complete |
code_block_selector |
str | CSS selector for code blocks |
rate_limit_seconds |
int | Minimum seconds between requests |
max_session_minutes |
int | Max session duration before re-auth |
timeout_seconds |
int | Per-request timeout |
Registered providers: qwen_chat (5s rate limit, 60min session), deepseek_chat (10s rate limit, 30min session), kimi (8s rate limit, 45min session).
extraction.py
extract_file_blocks(response): splits on^FILE:\s*(multiline), extracts filename from next line, extracts fenced code block that follows; returnslist[tuple[str, str]]extract_markdown_code_blocks(response): regex ```(\w*)\n(.*?)```` (DOTALL); returnslist[tuple[lang, content]]_extract_fenced_code(lines): internal helper; state machine over lines to extract content between ```` markers
Timeouts
| Driver | Timeout | Rationale |
|---|---|---|
| ClaudeCodeDriver | 900s | Interactive CLI, longer tasks |
| ClaudeAPIDriver | 1200s | Agentic mode writes many files |
| AiderDriver | 300s | DeepSeek backend, typically 30-60s |
| DeepSeekCoderDriver | Per HTTP client | Single API call |
| QwenDriver | 600s | Local CLI, variable performance |
| BrowserDriver (per provider) | 120-180s | Web UI response latency |
Functional Requirements
FR-001: AsyncCodeDriver Protocol
All drivers MUST implement the AsyncCodeDriver Protocol with signature execute(task: str, files: list[str] | None, is_fix: bool) -> tuple[bool, str].
FR-002: Driver Factory Routing
get_driver(volk: str, workspace: str | None) -> AsyncCodeDriver MUST read the provider from orchestrata.llm.config and return the correct driver instance.
FR-003: CLI Binary Detection
CLI drivers (ClaudeCodeDriver, ClaudeAPIDriver, AiderDriver, QwenDriver) MUST locate their binary via shutil.which with known fallback paths.
FR-004: Subprocess Timeout Enforcement
All CLI drivers MUST enforce timeouts using asyncio.wait_for around subprocess execution.
FR-005: Workspace File Scanning
ClaudeAPIDriver MUST scan the workspace post-execution for source files (.py, .toml, .cfg, .ini, .yaml, .yml), excluding .venv, __pycache__, .git, node_modules, .claude, design.
FR-006: DeepSeek API Integration
DeepSeekCoderDriver MUST call the DeepSeek HTTP API with model=deepseek-coder and use DEEPSEEK_API_KEY from environment.
FR-007: Browser Session Persistence
BrowserDriver MUST persist session state to .browser_state/<provider>_state.json and recover sessions across restarts.
FR-008: 3-Tier Browser Recovery
BrowserDriver MUST implement 3-tier recovery: Level 1 = page navigation, Level 2 = browser restart, with max 3 recovery attempts.
FR-009: Rate Limiting
BrowserDriver MUST enforce provider-specific rate_limit_seconds between requests.
FR-010: Code Extraction Utilities
extraction.py MUST provide extract_file_blocks() and extract_markdown_code_blocks() returning list of tuples.
FR-011: Unknown Provider Fallback
get_driver MUST log a warning and fall back to ClaudeAPIDriver for unrecognised providers.
FR-012: Runtime Protocol Checkability
AsyncCodeDriver MUST be decorated with @runtime_checkable for isinstance checks.
Error Conditions
| ID | Condition | Handling |
|---|---|---|
| ERR-001 | Binary not found | Return (False, "<tool> CLI not found") |
| ERR-002 | asyncio.TimeoutError | Return (False, "Timed out after Xs") |
| ERR-003 | Non-zero subprocess exit | Return (False, collected_output) |
| ERR-004 | No source files after ClaudeAPIDriver | Return (False, "No files generated") |
| ERR-005 | Browser init failure | Return (False, "Browser init failed: <exc>") |
| ERR-006 | All browser recovery attempts exhausted | Return (False, "All recovery attempts exhausted") |
| ERR-007 | No code blocks in browser response | Return (False, "No code blocks found in response") |
| ERR-008 | Unknown provider in get_driver | Log warning, fall back to ClaudeAPIDriver |
| ERR-009 | Unknown browser provider in get_provider_config | Raise ValueError |
| ERR-010 | Missing DeepSeek API key | Return (False, error message) |
| ERR-011 | HTTP API connection error | Return (False, "API connection failed: <exc>") |
| ERR-012 | Browser session timeout | Return (False, "Session expired, max session time reached") |
| ERR-013 | Invalid workspace path | Raise ValueError or return (False, "Invalid workspace") |
| ERR-014 | Playwright selector timeout | Return (False, "Element selector timeout: <selector>") |
| ERR-015 | File write permission denied | Return (False, "Permission denied writing: <filename>") |
| Environment Variable | Purpose | Default |
|---|---|---|
ORCHESTRATA_WORKSPACE |
Default workspace for all drivers | /home/mohamed/workspace |
DEEPSEEK_API_KEY |
API key for DeepSeekCoderDriver | (required if provider=deepseek) |
Non-Functional Requirements
Performance
- BrowserDriver request completion: < 180 seconds per provider
- CLI driver timeout: 300-1200 seconds depending on driver
- Factory get_driver() instantiation: < 1 second
Scalability
- Support 7+ concurrent drivers without resource contention
- Browser sessions: maximum 3 parallel per provider to avoid rate limits
- Memory usage: < 100MB per browser session
Reliability
- BrowserDriver 3-tier recovery success rate: > 95%
- Session persistence: 99% recovery after restart
- Code extraction: 99.5% accuracy for FILE: format
Observability
- All drivers log execution time, success/failure status
- BrowserDriver logs recovery attempts and session state
- Factory logs provider fallback decisions
Maintainability
- Provider configuration centralized in browser_providers.py
- CSS selectors updateable without code changes
- Protocol-based design enables new drivers without modifying callers
Examples
Example 1: Basic Code Generation
from orchestrata.drivers import get_driver
# Get driver based on volk configuration
driver = get_driver(volk="J:Junior", workspace="/home/mohased/workspace/project")
# Execute code generation task
success, output = await driver.execute(
task="Create a FastAPI endpoint for user authentication",
files=["main.py", "auth.py"],
is_fix=False
)
if success:
print("Code generated successfully")
print(output)
Example 2: Fix Existing Files
driver = get_driver(volk="K:CodeDirector")
# Ask for fixes to existing files
success, output = await driver.execute(
task="Fix the SQL injection vulnerability in this code",
files=["user_controller.py", "database.py"],
is_fix=True
)
Example 3: Code Extraction
from orchestrata.drivers.extraction import extract_file_blocks
raw_response = """
FILE: models/user.py
```python
class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
"""
Extract code blocks
blocks = extract_file_blocks(raw_response)
Returns: [("models/user.py", "class User:\n def init(self, id: int, name: str):\n self.id = id\n self.name = name\n")]
##### Example 4: Browser Provider Configuration
```python
from orchestrata.drivers.browser_providers import BROWSER_PROVIDERS
# Access browser provider configuration
qwen_config = BROWSER_PROVIDERS["qwen_chat"]
print(f"Rate limit: {qwen_config.rate_limit_seconds}s")
print(f"Session timeout: {qwen_config.max_session_minutes}min")
# Use with BrowserDriver
from orchestrata.drivers import get_driver
driver = get_driver(volk="qwen_browser")
Dependencies
| Dependency | Version | Purpose |
|---|---|---|
| orchestrata-core | workspace | @traces decorator, traceability |
| orchestrata-llm | workspace | get_volk_provider for factory routing |
| playwright | >=1.40 (optional) | BrowserDriver headless Chromium |
| Python stdlib | asyncio, re, os, shutil, dataclasses, logging | All drivers |
Security
- API Key Handling: DeepSeek API key MUST be retrieved from environment variable
DEEPSEEK_API_KEY, never hardcoded - Credential Storage: Browser session state JSON files MUST NOT be committed to version control (added to .gitignore)
- Subprocess Safety: CLI drivers MUST use
shutil.whichfor binary lookup, never user-provided paths - Input Sanitization: Driver task prompts SHOULD be treated as untrusted input; no shell execution beyond defined CLI arguments
- Browser Sandbox: Playwright BrowserDriver runs in headless mode with default sandboxing enabled
- Workspace Access: Drivers MUST only access configured workspace paths; path traversal prevention required
Governance
- Version Policy: Follows Semantic Versioning (MAJOR.MINOR.PATCH)
- Breaking Changes: Any change to AsyncCodeDriver Protocol signature requires MAJOR version bump
- Deprecation Cycle: Deprecated drivers remain for 2 minor versions with warnings
- Provider Deprecation: Browser providers with >30% failure rate for 7 days flagged for review
Testing
- Unit Tests: Each driver MUST have unit tests covering happy path and error conditions
- Integration Tests: BrowserDriver requires integration tests with real browser automation
- Mock Strategy: No mocking for external CLI/API calls; use real implementations or test doubles
- Coverage Target: Minimum 80% line coverage for driver modules
- Error Scenario Tests: Each ERR-001 through ERR-015 condition MUST have corresponding test case
Integration Points
| Component | Integration Type | Description |
|---|---|---|
| orchestrata-llm | Import | Uses get_volk_provider() for factory routing |
| orchestrata-core | Import | Uses @traces decorator for traceability |
| SPEC-020 (Claude Code Harness) | Consumer | Uses ClaudeCodeDriver, ClaudeAPIDriver |
| SPEC-027 (Junior Volk) | Consumer | Uses get_driver for per-file tasks |
| SPEC-029 (CodeDirector) | Consumer | Uses get_driver for swarm coordination |
| SPEC-011 (Provider Manager) | Consumer | get_volk_provider source |
| SPEC-028 (LLM Client) | Consumer | Shares LLM config abstractions |
| Playwright | Runtime | Browser automation library (optional) |
Success Criteria
- [ ] AsyncCodeDriver Protocol implemented and runtime-checkable
- [ ] 7 concrete drivers implemented: ClaudeCodeDriver, ClaudeAPIDriver, AiderDriver, DeepSeekCoderDriver, QwenDriver, BrowserDriver (3 providers)
- [ ] get_driver factory routes all 7+ provider strings correctly
- [ ] All drivers pass timeout enforcement tests
- [ ] BrowserDriver 3-tier recovery functional
- [ ] Code extraction utilities parse FILE: and markdown blocks accurately
- [ ] 80%+ test coverage achieved
- [ ] All ERR-001 through ERR-015 conditions have test coverage
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0.0 | 2026-04-04 | Mohamed | Initial specification |
| 1.1.0 | 2026-04-09 | Mohamed | Added User Scenarios, Data Models, Functional Requirements (FR-001-012), Error Conditions (ERR-001-015), Non-Functional Requirements, Examples, Security, Governance, Testing, Integration Points, Success Criteria, Revision History |
All public classes and functions carry @traces decorator referencing SPEC-005 (Multi-Agent Coordination) user stories:
- CLI drivers:
spec="005", story="US-007", reqs=["REQ-005-US-007-001"] - Browser driver and extraction:
spec="005", story="US-010", reqs=["REQ-005-US-010-001"]
SPEC-025 formalises the drivers as a standalone specification; traceability decorators will be updated to reference spec="025" in the next implementation pass.
Related Specifications
| Spec | Relationship |
|---|---|
| SPEC-005 (Multi-Agent Coordination) | Origin of driver user stories US-007, US-010 |
| SPEC-020 (Claude Code Harness) | Primary consumer of ClaudeCodeDriver and ClaudeAPIDriver |
| SPEC-027 (Junior Volk) | Junior uses get_driver to dispatch per-file tasks |
| SPEC-029 (CodeDirector Volk) | CodeDirector swarm coordinator uses get_driver |
| SPEC-011 (Provider Manager) | Provides get_volk_provider used by the factory |
| SPEC-028 (LLM Client) | Provides LLM config and provider abstractions |
Observability
Logging Requirements
- Component Logging
- Log all major operations with appropriate log levels
- Include correlation IDs for request tracing
- Log performance metrics for critical operations
- Log errors with full context for debugging
Metrics Requirements
- Performance Metrics
- Operation duration histograms
- Request/error rate counters
- Resource utilization gauges
- Business metrics relevant to domain
Tracing Requirements
- Distributed Tracing
- Trace propagation across component boundaries
- Span attributes for domain-specific context
- Trace sampling based on operational needs
Alerting Requirements
- Critical Alerts
- Error rate spikes
- Performance degradation
- Resource exhaustion
- Business metric anomalies
Dashboard Requirements
- Operational Dashboard
- Real-time system health
- Performance trends
- Error analysis
- Resource monitoring