AI_orchestrator
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Fail
- rm -rf — Recursive force deletion command in config.py
- network request — Outbound network request in dashboard.py
- rm -rf — Recursive force deletion command in doctor.py
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Autonomous orchestrator for Claude/Gemini/Codex CLIs. Markdown-driven queue with multi-provider fallback, policy gating, and full audit trail.
AI Orchestrator
Autonomous task executor for claude, gemini, and codex CLI tools — driven by a Markdown queue file, with multi-provider fallback, Telegram control, and a security/approval layer.
Goal: Run routine work (code, reviews, tests, docs, refactors) from a Markdown queue without managing API keys in your project. Uses existing CLI logins (OAuth/subscription).
Why this exists
Routine engineering work — code reviews, refactors, test loops, documentation sync, security audits — is the ideal candidate for autonomous execution by an LLM CLI. But the existing tools fall apart at the operational layer:
- No orchestration across providers. Claude, Gemini, and Codex are each excellent in isolation, but no tool routes tasks across all three with capacity-aware fallback. When Claude hits a 5 h block, the workflow shouldn't stop — it should silently fail over.
- Long-running autonomous loops need an audit trail. Every dev-loop iteration, every security finding, every retry decision should be inspectable after the fact, not lost to stdout.
- State should live in plain Markdown, not in a database. A human edits the queue with a text editor, version-controls it in Git, and reads every result alongside the task that produced it.
- Subscription-driven CLIs (OAuth, no API keys) are the cheapest way to run an autonomous agent for a single user — provided you have a layer on top that respects quota windows, retries 429s, and knows when not to run.
This is the orchestrator built around that reality.
Engineering highlights
The codebase prioritises auditability, safety, and operational fitness over feature breadth. If you're evaluating the architecture rather than the feature list:
- ~1720 tests / ~90 s — full pytest suite covers queue parsing, dispatcher fallback, policy classification, provider mocks, stdin delivery verification, parallel execution, idempotency, quota calibration + SoTH state + live estimation, and per-tool phase logic. Tests are synchronous (no asyncio), pure stdlib + pytest fixtures, no live network calls.
- Defence in depth.
scripts/safety_hook.pyis a Claude CodePreToolUsehook that hard-denies destructive commands (rm -rf, force-push,DROP TABLE, raw disk writes, …) even under--dangerously-skip-permissions. A second layer (SAFETY_RULES) is injected into Gemini/Codex prompts. CWD validation againstALLOWED_CWD_ROOTSblocks writes outside whitelisted roots. - Three-tier approval policy.
policy.pyclassifies every task asAUTO,APPROVE, orDENY.APPROVEtasks block until a Telegram/approvearrives;DENYnever runs. Per-tool budgets and stop conditions (max_iterations,max_runtime_sec,max_files_touched,reporting_path) are declared in a YAMLtool_contracts:section with schema validation at startup — one auditable place for every guard rail. - Operational resilience. Three-tier HTTP 429 fallback (cclimits → local JSONL → optimistic), provider cooldowns with model-alias routing, OAuth-aware capacity polling (5 min active / 10 min idle, matching
cclimits --cache-ttl), and a crash-resistant PowerShell watchdog with exponential backoff and Telegram alerts on every restart. - Auditability built in. Every task end emits a structured
logs/runs.jsonlrecord (19-category failure taxonomy:rate_limit,timeout,auth_error,model_refusal, …), per-tool action traces in{cwd}/.<tool>/traces/*.jsonl, an offline queue linter (--lint-queue, exit codes 0/1/2 for CI gating), and an 18-check--doctorwith--fix --yesauto-repair. - Prompt-cache aware. Stable prompt prefixes,
--exclude-dynamic-system-prompt-sectionsto freeze the system prompt for cache hits, opt-in Claude session reuse (--session-id/--resume) for ~30–50 % token savings across multi-phase tools, and billing analytics with weighted cost (input × 1.0 + cache_creation × 1.25 + cache_read × 0.1 + output × 5.0) and per-task cache-hit rate. - Observability. Standalone HTTP analytics dashboard (port 8211, Chart.js, 60 s refresh) backed by
logs/runs.jsonl. Live "Active Runs" panel (30 s refresh) shows currently-running tool iterations, phase, tokens (in / out / cache_read) and elapsed time via the centralActiveRunRegistryinlogs/active_runs/. Daily Telegram status recap (07:00) summarises the previous 24 h — tasks done/failed, provider breakdown, pending approvals, blocked tasks. - Calibrated quota model. Phase-0 telemetry (
logs/quota-calibration.csv) paired everycclimitspoll with locally-aggregated JSONL token counts per Anthropic 5 h / 7 d window; ~6 days of data selected theio_onlymodel (input + output — cache tokens are negligible to the rate limit). Phase 1 writes a single-source-of-truthlogs/cc_quota_state.jsoneach poll (consumed by the Claude Code statusline and--check-limits) and feeds the calibrated per-window factors into the 429-fallback estimator, reducing reliance on the undocumented, rate-limitedcclimitsendpoint.
Architecture details → docs/architecture/components.md (per-module specs), docs/architecture/patterns.md (patterns and invariants).
Features
- Multi-provider routing with fallback (
Claude → Gemini → Codex) - Two opt-in providers outside the fallback chain: OpenRouter (pay-per-token,
#openrouter/#or_*) for cheap single-call work, and Mistral Vibe (#vibe,#second_opinion:vibe) as a read-only second non-Claude reviewer. Both are registered only when their prerequisite exists (API key / binary onPATH); a#vibetag without the CLI parks the task rather than handing a review job to a file-writing executor. - Capacity checking via
cclimits(with local JSONL fallback on HTTP 429) - Retry handling on rate limits / provider failures
- Obsidian-compatible queue with
cwd:,#tool:,#agent:,#parallel,#worktree,#keep-worktree,#shutdown,#approve:*tags #worktreeparallel isolation (P1): each CWD group of a#parallelparent runs in its owngit worktreeunder.worktrees/parallel-<hash>. Failed groups retain the worktree for inspection.#keep-worktreesuppresses cleanup on success.- Tool loops:
dev-loop,review-loop,test-loop,research-qa,security-audit,deep-security-audit,critical-review,knowledge-transfer,scientific-investigation,brainstorm,pr-babysitter #tool:pr-babysitter(P2/P5): polls open PRs viagh, queues#tool:dev-loopfix-tasks on new comments / CI failures. Two modes via#pr-mode:queue|report-only— report-only sends a Telegram summary with/pr-fix <repo>#Nand/pr-ignore <repo>#Nslash commands instead of writing the queue directly.- CI-Watcher heartbeat (P4):
check-ci-failurespollsgh run list --status=failureperCI_WATCHER_REPOSwhitelist and queues one fix-task per new failing commit (dedup by(repo, headSha), 2h cooldown). Optional repo→local-path mapping viaCI_WATCHER_REPO_PATHS. - Status-Recap heartbeat (P6): daily 07:00 Telegram summary — tasks done/failed, provider breakdown, pending approvals, blocked tasks, top successes/failures of the last 24h. Idle day → one-liner.
- Tool Contracts (P3):
tool_contracts:section inpolicy.yamldeclares per-tool budgets (max_iterations,max_runtime_sec,max_files_touched), stop conditions, and reporting paths in one auditable place.PolicyEngine.get_tool_contract(name)returns aToolContractdataclass; tools may fall back toconfig.TOOL_*constants for fields the contract omits (staged migration). - Skills /
SKILL.mddiscovery with requirements gating - Memory (TF-IDF + temporal decay) for recurring tasks
- Execution profiles (provider order, allowed skills, timeout, policy overrides)
- Execution policy (
AUTO/APPROVE/DENY) with Telegram approval flows - Telegram listener (queue control, status, plain-text AI chat)
- Heartbeat + Doctor (monitoring / onboarding checks)
- Reliability layer (Tier 5): queue linter (
--lint-queue), idempotency keys for external triggers, slash-commands (/review,/dev,/security,/audit,/critique,/brainstorm), schedule tags (#at:,#every:), machine-readable run summaries (logs/runs.jsonl), 19-category failure taxonomy, per-tool preflight hooks (deterministic context collection), queue-healing with/unblock//drop//retry, draft-only skill suggester, progressive skill loading - Analytics web dashboard (Chart.js, port 8211)
- Calibrated quota model (Phase 0 + 1): Phase-0 telemetry (
logs/quota-calibration.csv) selected theio_onlytokens_per_pctmodel (input + output). Phase 1 persists a SoTHlogs/cc_quota_state.jsonper poll (read by the statusline +--check-limits) and uses calibrated per-window factors in the 429-fallback estimator. The undocumentedcclimitsendpoint stays the calibration anchor (polled every 5–10 min), not the per-reading source. Details →docs/architecture/components.md. SOUL.mdas central prompt/personality configuration- Anthropic prompt-cache optimization: static system-prompt (cwd/git-status moved to first user message via
--exclude-dynamic-system-prompt-sections), stable prompt prefixes, billing analytics with cache-hit-rate - Optional Claude session reuse (
CLAUDE_SESSION_ENABLED=true):dev-loop,review-loop, same-providercritical-review, anddeep-security-auditshare Claude--session-id/--resumeacross phases for cross-call cache hits
Requirements
- Python
3.10+ cclimitsCLI (npm install -g cclimits)- Provider CLIs in
PATH:claude,codex(andgeminiunless you useGEMINI_API_KEYHTTP mode — the consumer Gemini CLI was retired 2026-06-18) - Valid authentication in each CLI (OAuth / subscription login); Gemini alternatively via a
GEMINI_API_KEY - Optional, opt-in only:
vibe(Mistral Vibe CLI) as a second non-Claude reviewer, and anOPENROUTER_API_KEYfor pay-per-token single-call tasks. Neither is required, and neither ever enters the default fallback chain.
Installation
git clone https://github.com/swDomass/AI_orchestrator.git
cd AI_orchestrator
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your vault path and optional Telegram credentials
requirements.txt includes:
pyyaml>=6.0claude-monitor>=3.0.0(optional — enables local JSONL fallback for Claude HTTP 429; requiresCLAUDE_PLANin.env)
Configuration
All configuration lives in .env (auto-loaded, no external dotenv library needed).
| Variable | Required | Default | Description |
|---|---|---|---|
ORCH_VAULT_PATH |
Yes | — | Path to your Obsidian vault (or any directory) |
ORCH_QUEUE_FILE |
No | <vault>/99_System/AI/agent-queue.md |
Direct path to queue file |
ALLOWED_CWD_ROOTS |
No | (allow all) | Semicolon-separated list of root paths; cwd: tags are validated against these. Leave empty to allow all paths. |
TELEGRAM_BOT_TOKEN |
No | — | Telegram bot token (from @BotFather) |
TELEGRAM_CHAT_ID |
No | — | Your Telegram chat ID |
MIN_CAPACITY_PERCENT |
No | 10 |
Global minimum remaining capacity (%) before a provider is skipped |
CLAUDE_FIVE_HOUR_MIN_CAPACITY_PCT |
No | 10 |
Per-window override for Claude 5h window |
CLAUDE_SEVEN_DAY_MIN_CAPACITY_PCT |
No | 3 |
Per-window override for Claude 7d window |
CODEX_PRIMARY_MIN_CAPACITY_PCT |
No | 10 |
Per-window override for Codex primary |
CODEX_SECONDARY_MIN_CAPACITY_PCT |
No | 3 |
Per-window override for Codex secondary |
CLAUDE_PLAN |
No | — | Claude subscription plan for local 429 fallback: pro, max5, max20, custom |
GEMINI_API_KEY |
No | — | Google AI Studio key. When set, the Gemini provider uses the HTTP REST API instead of the CLI (the consumer Gemini CLI was shut down 2026-06-18). HTTP mode stays in the fallback chain but has no pollable quota — availability is cooldown-driven. Without a key, falls back to the legacy gemini CLI (Standard/Enterprise only). |
GEMINI_BASE_URL |
No | https://generativelanguage.googleapis.com/v1beta |
Override for testing or a proxy |
GEMINI_DEFAULT_MODEL |
No | gemini-3.5-flash |
Model for HTTP mode without a #gemini_* tag (free-tier-safe GA Flash) |
GEMINI_MAX_OUTPUT_TOKENS |
No | 16384 |
Output cap for HTTP mode; generous because gemini-3.x thinking tokens count toward it |
OPENROUTER_API_KEY |
No | — | OpenRouter API key. When set, enables #openrouter/#or_* tags as an opt-in pay-per-token provider for non-agentic tasks (heartbeat checks, summaries). Never enters the default fallback chain. |
OPENROUTER_BASE_URL |
No | https://openrouter.ai/api/v1 |
Override for testing or self-hosted proxy |
OPENROUTER_DEFAULT_MODEL |
No | minimax/minimax-m2.5:free |
Model used by #openrouter without a specific #or_* alias |
VIBE_MAX_PRICE_USD |
No | 0.50 |
Per-run cost ceiling for the Mistral Vibe provider (vibe interrupts itself above it) |
VIBE_MAX_TURNS |
No | 12 |
Assistant-turn budget for tool-enabled Vibe runs (read_file/grep only) |
VIBE_READONLY_MAX_TURNS |
No | 1 |
Turn budget when all tools are disabled — a single turn is all that can happen |
ORCH_QUOTA_LIVE_ESTIMATE |
No | false |
Phase 2: decrement the cached quota snapshot by a live per-task estimate between cclimits polls |
ORCH_QUOTA_AUTO_RECALIBRATE |
No | false |
Requires the flag above: re-derive the per-window tokens_per_pct factors daily from logs/quota-calibration.csv (min-samples + clamp guarded) |
DASHBOARD_PORT |
No | 8211 |
Port for the analytics web dashboard (auto-falls back to a free port if taken/Windows-reserved) |
TELEGRAM_MAX_TASK_LENGTH |
No | 500 |
Max characters for /task command |
CLAUDE_SESSION_ENABLED |
No | false |
Opt-in: Claude --session-id/--resume across tool phases for prompt-cache reuse. Off = today's stateless behaviour. |
ORCH_SESSION_RETENTION_DAYS |
No | 14 |
Heartbeat session-cleanup retention for orchestrator-created session JSONL files in ~/.claude/projects/. Whitelist via sidecar registry. |
PR_BABYSITTER_REPOS |
No | (empty) | Semicolon-separated owner/name list for #tool:pr-babysitter without an explicit #repos: tag. Empty = tool requires the tag. |
PR_BABYSITTER_QUEUE_COOLDOWN_HOURS |
No | 1 |
Suppresses duplicate dev-loop queue items for the same PR within the window. |
CI_WATCHER_REPOS |
No | (empty) | Semicolon-separated owner/name list for the check-ci-failures heartbeat. Empty list disables the handler. |
CI_WATCHER_REPO_PATHS |
No | (empty) | Optional owner/name=local/path[;...] mapping so the auto-generated queue item carries a usable cwd: tag. |
CI_WATCHER_QUEUE_COOLDOWN_HOURS |
No | 2 |
Cooldown per (repo, headSha) before re-queueing a CI failure. |
See .env.example for a complete annotated template.
Quick Start
# Validate setup (CLIs, vault, queue, Telegram, skills, ...)
python orchestrator.py --doctor
# Process queue once
python orchestrator.py
# Watch mode (heartbeat + Telegram listener + auto-retry)
python orchestrator.py --watch
Crash-Resistant Watchdog (Windows)
For long-running unattended operation, use the PowerShell watchdog wrapper. It restarts the orchestrator on crashes with exponential backoff and sends a Telegram alert on every restart. Works on both Windows PowerShell 5.1 and PowerShell 7+:
# Foreground (visible console)
pwsh -File run_orchestrator.ps1
# Or, if you only have Windows PowerShell:
powershell.exe -File run_orchestrator.ps1
# Background — survives terminal close
Start-Process pwsh -ArgumentList "-File run_orchestrator.ps1" -WindowStyle Hidden
Behaviour:
- Exit 0 (Ctrl+C,
#shutdown) ends the loop cleanly - Non-zero exit → restart with backoff (10 s → 5 min cap)
- ≥5 crashes / 10 min → 30 min cooldown + Telegram alert
- All restarts logged to
logs/watchdog.log(rotated at 10 MB →watchdog.log.1) - Telegram credentials read directly from
.env(no Python helper); URL-encodes the token, strips UTF-8 BOM, honors# inline commentsper Python's dotenv rules
CLI Commands
python orchestrator.py # Single queue pass
python orchestrator.py --watch # Continuous mode
python orchestrator.py --dry-run # Parse queue without executing
python orchestrator.py --check-limits # Show provider capacity
python orchestrator.py --list-tools # Show available #tool: handlers
python orchestrator.py --dashboard # Launch analytics dashboard
python orchestrator.py --doctor # Validate setup
python orchestrator.py --doctor --fix # Auto-fix issues
python orchestrator.py --doctor --fix --yes
python orchestrator.py --lint-queue # Validate agent-queue.md (no execution)
--lint-queue (offline queue validation)
Runs a pure-validation pass over agent-queue.md. No LLM calls. Catches:
- Invalid / missing
cwd:(path doesn't exist or outsideALLOWED_CWD_ROOTS) - Unknown
#tool:<name> - Unknown model alias (
#claude_*,#gemini_*,#codex_*,#or_*) - Cross-provider model leakage (
#claude_opus+ explicit#gemini= error) - Duplicate
#id:values in the open queue #needs:referencing IDs that will never resolve (warning)#openrouter/#or_*withoutOPENROUTER_API_KEYconfigured (warning — task falls back to default chain)#parallelwith 0-1 subtasks (warning) or shared CWD (info)
Not yet covered: #vibe* tags (neither the unknown-alias check nor a "CLI missing → task will be parked" warning) — the linter shares extract_model_tag() with the queue parser and inherits the gap described under Known Limitations.
Exit codes: 0 = clean, 1 = warnings only, 2 = errors. Wire into CI / pre-commit if you have a shared queue file.
Queue File Syntax
The queue is read from Markdown. Open tasks are standard checkbox lines:
- [ ] Fix bug in parser cwd:D:\projects\app #codex #timeout:10m
- [ ] Review + fix repo #tool:review-loop cwd:"D:\projects\my repo" #agent:work
- [ ] Fix login bug #tool:dev-loop cwd:D:\projects\app
- [ ] Add CSV export #tool:dev-loop cwd:D:\projects\app #agent:work
- [ ] Add OAuth2 flow #tool:research-qa cwd:D:\projects\app
- [ ] Architecture audit #tool:critical-review cwd:D:\projects\app
- [ ] Prüfe docs/plan.md #tool:critical-review #pass1:claude #pass2:codex cwd:D:\projects\app
- [ ] Security audit #tool:security-audit cwd:D:\projects\app
- [ ] Deep security audit #tool:deep-security-audit cwd:D:\projects\app
- [ ] Deep audit (no fix) #tool:deep-security-audit #no-fix cwd:D:\projects\app
- [ ] Deep audit with cross-expert dialog #tool:deep-security-audit #roundtable cwd:D:\projects\app
- [ ] Brainstorm pricing strategy #tool:brainstorm cwd:D:\projects\app
- [ ] Brainstorm with cross-provider personas #tool:brainstorm #cross-provider #top_n:7 cwd:D:\projects\app
The orchestrator automatically appends ## Results and ## Log sections to each task.
Supported Tags
| Feature | Syntax | Example |
|---|---|---|
| Force provider | #claude, #gemini, #codex, #vibe |
- [ ] Task #codex |
| Claude model | #claude_haiku, #claude_sonnet, #claude_opus |
- [ ] Task #claude_haiku |
| Gemini model | #gemini_pro, #gemini_flash, #gemini_flash_lite |
- [ ] Iterate #gemini_flash |
| Codex model | #codex_5 (gpt-5.6-sol), #codex_5_4 (gpt-5.6-terra), #codex_mini (gpt-5.6-luna) |
- [ ] Run #codex_mini |
OpenRouter model (opt-in, requires OPENROUTER_API_KEY) |
Free: #or_minimax_free, #or_deepseek_free, #or_qwen_free, #or_nemotron_free. Paid flagships: #or_glm, #or_kimi, #or_qwen, #or_deepseek, #or_minimax. Generic: #openrouter (default model). |
- [ ] Daily summary #or_minimax_free |
Vibe / Mistral (opt-in, requires the vibe CLI) |
#vibe — routes to Vibe using its configured model. Reviewer only: never writes files, never enters the fallback chain, and if the CLI is missing the task is parked rather than handed to an executor. |
- [ ] Second opinion #vibe |
| Vibe model choice | #second_opinion:vibe_medium (mistral-medium-3.5) / #second_opinion:vibe_small (devstral-small). Only as a #second_opinion: value — as bare task tags #vibe_medium/#vibe_small route to Vibe but do not force the model yet (see the MODEL_TAG_RE gap under Known Limitations). |
- [ ] Review #tool:review-loop #second_opinion:vibe_small |
| Run tool | #tool:<name> |
- [ ] Review #tool:review-loop |
| Restrict providers (task-level) | #tool_providers:<p1,p2> |
#tool_providers:claude,gemini |
| Working directory | cwd:<path> |
cwd:D:\projects\repo |
| Working directory with spaces | cwd:"<path>" |
cwd:"D:\My Projects\App" |
| Timeout (hard backstop, not aggressive deadline; for tools an upper cap) | #timeout:<n>[s|m|h] |
#timeout:30s, #timeout:15m, #timeout:1h |
| Execution profile | #agent:<name> |
#agent:work |
| Parallel task | #parallel |
Parent task with indented subtasks |
| Task ID | #id:<name> |
- [ ] Build backend #id:build |
| Task dependency | #needs:<id1,id2> |
- [ ] Test #needs:build |
| One-time schedule | #at:<timestamp> |
- [ ] Review #at:2026-05-17T22:00 |
| Recurring schedule | #every:<duration> |
- [ ] Daily review #every:24h |
| Time-of-day anchor | #at:<HH:MM> + #every:<Nd> |
- [ ] Daily brief #at:08:00 #every:24h |
| Skip stale slot | #freshonly |
- [ ] Daily brief #at:08:00 #every:24h #freshonly |
| Stale grace window | #grace:<duration> |
- [ ] Recap #at:19:00 #every:24h #freshonly #grace:4h |
| Shutdown after task | #shutdown |
- [ ] Backup #shutdown |
| Cross-provider pass | #pass1:<provider>, #pass2:<provider> |
#pass1:claude #pass2:codex |
| Preapproval | #approve:<category,...> |
#approve:push,publish |
| Second opinion (review-loop) | #second_opinion:<alias|provider> |
#second_opinion:codex, #second_opinion:vibe_small |
Known Limitations
Model tags: only 6 of 20 are enforced. queue_manager.MODEL_TAG_RE recognises#claude_haiku/_sonnet/_opus, #gemini_pro/_flash and #codex_mini. The other
fourteen — #gemini_flash_lite, #codex_5, #codex_5_4, #vibe_medium,#vibe_small and all nine #or_* — still route to the right provider via the
dispatcher, but the model is not forced: the task runs on that provider's default
model. #or_glm and #or_kimi therefore produce the same model today. The#second_opinion:<alias> path is unaffected — it resolves aliases separately and
honours every one of them. Fix vector: derive the pattern from the alias maps inconfig.py instead of hard-coding it, so it cannot drift again.
#pass1: / #pass2: accept only claude, gemini, codex. The
cross-provider critical-review tags are matched by their own regex, which
predates the opt-in providers — #pass2:vibe and #pass2:openrouter are ignored
rather than rejected. Use #second_opinion: for those.
Parallel Tasks (#parallel)
- [ ] Release prep #parallel #agent:work
- run tests #tool:test-loop cwd:D:\proj
- review code #tool:review-loop cwd:D:\proj
- update changelog cwd:D:\proj #codex
- Subtasks with the same
cwdrun sequentially within a group. - Subtasks with different
cwds run in parallel threads. - One subtask failing does not stop the others.
Task Dependencies (#id: / #needs:)
- [ ] Build backend #id:build cwd:D:\projects\app
- [ ] Run integration tests #id:tests #needs:build cwd:D:\projects\app
- [ ] Deploy to staging #needs:build,tests cwd:D:\projects\app
- A task with
#needs:stays blocked until all named IDs appear as[x](done) or[-](failed). - Blocked tasks are not removed from the queue — they stay open and are re-checked each cycle.
- The queue header shows
(N runnable, M blocked)when blocked tasks are present. [-](failed) tasks also unblock dependents — the downstream task decides how to handle failure.
Schedules (#at: / #every:)
- [ ] One-time review tonight #at:2026-05-17T22:00 #tool:review-loop cwd:D:\proj
- [ ] Daily review #every:24h #tool:review-loop cwd:D:\proj
- [ ] Daily brief, anchored at 08:00 #at:08:00 #every:24h cwd:D:\proj
- [ ] Daily brief, skip if missed #at:08:00 #every:24h #freshonly #grace:4h cwd:D:\proj
Both schedule tags reuse the existing retry primitive — no separate scheduler.
#at:<timestamp>delays the task until the given time. Formats:YYYY-MM-DDTHH:MM,YYYY-MM-DD HH:MM, orHH:MM(closest-day interpretation). For a one-time task (no#every:), it is marked[x]after the first fire.#every:<duration>turns the task into a recurring schedule. Units:s|m|h|d. On successful completion, the line is rewritten as open with a new<!-- retry: ... -->annotation instead of[x]— it fires again on schedule.- Anchored recurrence (no drift): combine
#at:<HH:MM>with a day-multiple#every:(e.g.#every:24h,#every:7d). The next run is computed as the next occurrence of the anchor time-of-day (notnow + duration), so a late boot never shifts the daily slot. The#at:anchor is preserved (normalized to bareHH:MM). Without an#at:anchor (or with a sub-day interval), the legacynow + durationbehavior applies and a stale one-time#at:is stripped. #freshonlymarks a recurring task whose run is only meaningful close to its slot (daily briefs). If the orchestrator was off and the slot is more than the grace window in the past, the task is realigned to its next slot without running — no late, stale fire. Tasks without#freshonlykeep catch-up semantics (a missed weekly maintenance run still executes on next start).#grace:<duration>sets how late after the anchor a#freshonlytask may still run before it counts as stale (default2h). Example:#grace:4hlets a 19:00 recap still fire on a 22:00 boot.- Pause / remove a recurring schedule = edit the queue file (delete the line, comment it out, or mark
[x]manually). - Missed runs: without
#freshonly, the retry time is in the past on next start → the task runs immediately (catch-up). With#freshonly, a stale slot is skipped and realigned. - Validated by
--lint-queue: malformed#at:,#every:and#grace:values are flagged as errors;#freshonlywithout#every:and#grace:without#freshonlyare warnings.
Retry Markers
- [ ] Task <!-- retry: 2026-02-26 23:10 -->
Built-in Tools
| Tool | Description |
|---|---|
dev-loop |
Research → Execute → Dual-Review loop (Code Quality + Issue Resolution). Both reviews must pass. Output in {cwd}/.dev-loop/. |
review-loop |
Iterative Review → Fix → Re-Review loop. Fixes ALL P1/P2/P3 findings. Max 20 iterations with infinite-loop detection. Optional drift-check (policy.yaml tool_phases.review-loop.drift_check_mode, default auto) injiziert eine Refocus-Warning in den nächsten Fix-Prompt, wenn der Reviewer in unrelated Refactoring abgedriftet ist. |
test-loop |
Iterative test / fix loop until tests pass or max iterations. |
research-qa |
Read-only pre-implementation research: Discovery → Analysis → Question catalogue. Output in {cwd}/.research-qa/. No code changes. |
knowledge-transfer |
Cross-domain knowledge transfer: Vault expertise → industry applications (via web search) → Obsidian idea note. |
critical-review |
3-pass adversarial review: analysis → challenge → synthesis. Reference a plan file to get {name}-v2.md. Cross-provider via #pass1:claude #pass2:codex. Output in {cwd}/docs/critical-review-*.md. |
security-audit |
Two-phase workflow: Audit (read-only) → Fix + pytest. Scans for hardcoded secrets, command injection, path traversal, unsafe deserialization, SSRF, and more. Output in {cwd}/docs/security-audit-*.md. |
deep-security-audit |
Multi-agent deep audit: 6 expert personas (pentester, architect, SAST, supply chain, data privacy, forensics) + CISO synthesis + optional fix. #no-fix skips fix phase. #roundtable inserts a Phase 6.5 dialogue where each persona reviews the others' findings (~6 extra subprocess calls, more robust CISO synthesis on conflicting findings). Output in {cwd}/docs/deep-security-audit-*.md. Structured action trace at {cwd}/.deep-security-audit/traces/<run_id>.jsonl. |
scientific-investigation |
Wissenschaftlicher Autopilot mit Audit-Trail. Pipeline (Plan v5, I0–I9): Framing + Pre-Registration → Multi-Persona Review (Author + Devils-Advocate + Methodiker) → Sub-Task-Execution-Loop → Synthesis mit Falsifikations-Tabelle → Mechanical & heuristic check → Engineering-Reviewer Rework → Final Telegram-Approval. Status-Tuple methodological_rigor=MEDIUM|LOW (HIGH strukturell ausgeschlossen). Output in {cwd}/docs/scientific-investigation-{ts}/ + audit-pack via scripts/build_audit_pack.py. |
pr-babysitter |
Polls open GitHub PRs via gh and reacts to new review comments / CI failures. #pr-mode:queue (default) appends a #tool:dev-loop fix task, #pr-mode:report-only sends a Telegram summary with /pr-fix + /pr-ignore. Tool itself is read-only. |
brainstorm |
Multi-persona Round-Table mit domain-aware Personas: LLM analysiert das Thema und wählt 4–6 themenspezifische Personas, die in Cross-Pollination-Runden Ideen produzieren bis Konvergenz (TF-IDF Cluster-Wachstum < 20 %) oder Hard-Cap (5 Iterationen). #cross-provider verteilt Personas Round-Robin über alle verfügbaren Provider. Synthesizer ranked Top-N (default 5) mit Pro/Contra/Nächster-Schritt. Output in {cwd}/docs/brainstorm-*.md, State + Per-Iteration-Files in {cwd}/.brainstorm/{ts}/. |
python orchestrator.py --list-tools
Dev-Loop (#tool:dev-loop)
Phase 1 — Research + Plan (merged into ONE subprocess call)
Reads relevant code, understands the problem/feature,
AND produces the implementation plan in the same response.
Web search only if local sources are insufficient.
→ Saved to {cwd}/.dev-loop/research-and-plan.md
→ State persisted under phase=research_and_plan_done for capacity-resume.
Phase 2 — Execution
Implements the solution based on the merged research+plan output.
On iteration > 1: includes findings from both prior reviews.
Phase 3a — Code Quality Review (P1/P2/P3, read-only)
Checks: Correctness, Clean, Secure, Performant, Maintainable,
Testable, Robust, Documented, Compliant.
P1/P2 = blocking | P3 = non-blocking
Re-reads `git diff` fresh — does NOT trust pinned context.
Phase 3b — Issue Resolution Review (RESOLVED/PARTIAL/UNRESOLVED, read-only)
Checks only: Does the code solve the original task 100%?
Ignores code quality entirely. Re-reads `git diff` fresh.
→ Both reviews must pass → loop ends, no auto-push.
→ Per-iteration output in {cwd}/.dev-loop/round-NNN.md
→ Final summary: {cwd}/.dev-loop/summary.md
Phase B opt-in: When CLAUDE_SESSION_ENABLED=true, all phases share a Claude session (--session-id / --resume) for cross-call prompt-cache hits. Iteration cap of 5 per session triggers a rollover to a fresh UUID; explicit findings re-injection in the exec prompt makes the rollover seamless.
Timeout configuration (config.py):
| Constant | Default | Phase |
|---|---|---|
TOOL_DEV_RESEARCH_TIMEOUT_SEC |
3600 (60 min) | Research portion of merged Phase 1 |
TOOL_DEV_PLAN_TIMEOUT_SEC |
1800 (30 min) | Plan portion of merged Phase 1 (added to research timeout) |
TOOL_DEV_EXEC_TIMEOUT_SEC |
7200 (2 h) | Execution |
TOOL_DEV_QUALITY_REVIEW_TIMEOUT_SEC |
3600 (60 min) | Quality Review |
TOOL_DEV_RESOLUTION_REVIEW_TIMEOUT_SEC |
1800 (30 min) | Resolution Review |
Timeout / Liveness-Watchdog
CLI provider calls run through a liveness/hang watchdog (providers/process_runner.py) instead of a raw wall-clock deadline. A run that keeps making progress runs to completion; only a truly frozen process is killed — with a real process-tree kill (Windows taskkill /F /T, POSIX killpg), unlike subprocess.run which orphaned the real grandchild and then blocked on it.
Semantics change: #timeout: / profile timeout_minutes now set the HARD backstop (absolute upper bound for a progressing run), not an aggressive deadline. For iterative tools the value is an upper cap only and never raises per-phase caps above the TOOL_*_TIMEOUT_SEC constants; total tool runtime is bounded by ToolContract.max_runtime_sec.
| Constant | Default | Meaning |
|---|---|---|
TASK_TIMEOUT_SEC |
5400 (90 min) | Hard backstop per CLI call (env-overridable) |
TASK_IDLE_TIMEOUT_SEC |
300 (5 min) | Idle/hang detector for Claude (tool-aware: a running tool_use pauses the timer) |
CLI_IDLE_TIMEOUT_NO_LIVENESS_SEC |
1200 (20 min) | Idle detector for Gemini/Codex (byte-only, conservative — covers a long single tool phase) |
MAX_HANG_RETRIES |
2 | Idle-kills (error="hang") are requeued with a short backoff up to this many times, then the task is BLOCKED (not quota-reset-retried forever) |
HANG_RETRY_BACKOFF_SEC |
300 (5 min) | Backoff before requeueing a hung task |
TOOL_DEFAULT_MAX_RUNTIME_SEC |
3600 (60 min) | Fallback total-runtime deadline for an iterative tool when its ToolContract omits max_runtime_sec |
Research-QA (#tool:research-qa)
Phase 1 — Discovery
Explores codebase: docs, directory structure, relevant source files,
tests, configs, git history. No code is changed.
→ Saved to {cwd}/.research-qa/01-discovery.md
Phase 2 — Analysis
Deep analysis: 2–3 implementation approaches (pros/cons/effort/risk),
security, performance, testing strategy, risks, edge cases.
→ Saved to {cwd}/.research-qa/02-analysis.md
Phase 3 — Questions
Prioritised question catalogue (8–20 questions) with:
- [BLOCKING] markers for critical blockers
- Concrete code references
- Suggested options (Option A / Option B)
Categories: Requirements, Architecture, Scope, Technical Unknowns,
Risk & Rollback, Testing & Validation.
→ Saved to {cwd}/.research-qa/03-questions.md
→ Combined document: {cwd}/.research-qa/research-qa-complete.md
→ No code changes — pure analysis and questions.
Critical Review (#tool:critical-review)
3-pass adversarial review with optional cross-provider support:
Pass 1 — Analysis
Radical-honesty review: concept, architecture, code quality,
operational reality, methodology, blind spots.
→ Saved to {cwd}/docs/critical-review-*-pass1.md
Pass 2 — Adversarial Challenge
A different persona challenges Pass 1's findings: missed angles,
overclaims, underclaims, contradictions.
Can use a different provider for real perspective diversity.
Pass 3 — Synthesis (only when plan file referenced)
Produces an improved version of the plan based on both reviews.
→ Saved to {plan_dir}/{name}-v2.md
→ Combined report: {cwd}/docs/critical-review-YYYYMMDD-HHMMSS.md
Usage examples:
# Review-only (no plan file → 2 passes)
- [ ] Review auth module #tool:critical-review cwd:D:\projects\app
# Plan review with improved output (3 passes)
- [ ] Prüfe docs/plan.md #tool:critical-review cwd:D:\projects\app
# Cross-provider (Claude analyzes, Codex challenges)
- [ ] Prüfe docs/plan.md #tool:critical-review #pass1:claude #pass2:codex cwd:D:\projects\app
# Same provider for both passes
- [ ] Prüfe [[MyPlan]] #tool:critical-review #pass1:claude #pass2:claude cwd:D:\projects\app
Plan files can be referenced as file paths (docs/plan.md) or wikilinks ([[MyPlan]]).
Brainstorm (#tool:brainstorm)
Multi-persona round-table with domain-aware personas — the LLM picks 4–6 themenspezifische Rollen (z. B. für Pricing: Daten-Analyst, Boutique-Verkäuferin, Mitbewerber, Braut-Kundin), die in Cross-Pollination-Runden Ideen produzieren und gegenseitig challengen.
Phase 0 — Topic-Analyse + Persona-Generierung
LLM analysiert das Thema und schlägt 4–6 unique Personas vor
(kebab-case keys, je system_prompt ≥ 100 chars, paarweise distinct).
→ Sequentielle Validierung in parse_personas (Count, Keys, Prompt-Länge).
Phase 0.5 — Provider-Allocation
Default: alle Personas auf Primary-Provider.
Mit #cross-provider: Round-Robin über (claude, gemini, codex, openrouter).
Degradiert sauber auf primary-only wenn keine Cross-Provider verfügbar.
Phase 1 — Initial Idea Generation
Jede Persona unabhängig: bis zu 10 Ideen aus ihrer spezifischen Perspektive.
Output pro Persona in {cwd}/.brainstorm/{ts}/iteration-1-{key}.md.
Phase 2 — Cross-Pollination (iterativ)
Jede Persona sieht die Ideen der anderen + ihre eigenen,
contributes Aufbau-/Synthese-/Challenge-/Gap-Ideen.
K — Konvergenz-Check (deterministisch, kein LLM-Call)
TF-IDF Jaccard-Cosine Clustering (Threshold 0.40).
Stop wenn neue Cluster < 20 % vom Total. Hard-Cap 5 Iterationen.
Phase 3 — Synthese + Ranking
Synthesizer (Primary-Provider) wählt Top-N (default 5)
mit Pro/Contra/Nächster-Schritt aus allen Clustern.
→ Final-Report: {cwd}/docs/brainstorm-YYYYMMDD-HHMMSS.md
→ State + Iterations: {cwd}/.brainstorm/{ts}/
→ Trace: {cwd}/.brainstorm/traces/<run_id>.jsonl
Usage examples:
# Default: alle Personas auf Primary, 5 Iterationen max
- [ ] Brainstorm Pricing-Strategie WhiteLady #tool:brainstorm cwd:D:\projects\whitelady
# Cross-provider Diversität: jeder Persona ein anderes LLM
- [ ] Marketing-Ideen #tool:brainstorm #cross-provider #top_n:7 cwd:D:\projects\whitelady
# Persona-Count und Iter-Cap überschreiben
- [ ] Feature-Priorisierung #tool:brainstorm #min_personas:5 #max_personas:5 #max_iterations:3 cwd:D:\projects\app
Tags:
| Tag | Default | Range | Wirkung |
|---|---|---|---|
#cross-provider |
off | – | Round-Robin über alle verfügbaren Provider statt primary-only |
#max_iterations:N |
5 | 1–10 | Hard-Cap auf die Konvergenz-Schleife |
#top_n:N |
5 | 1–20 | Anzahl Top-Ideen im finalen Ranking |
#min_personas:N |
4 | 2–10 | Untergrenze Persona-Count (LLM muss ≥ N liefern) |
#max_personas:N |
6 | 2–10 | Obergrenze Persona-Count (LLM darf ≤ N liefern) |
Tradeoffs:
#cross-providerskaliert die Kosten linear mit Persona-Count und Iterationen — Faustregel: 5 Personas × 3 Iter × 2 Phasen = ~30 LLM-Calls quer über alle Provider. Default-off ist bewusst.- Konvergenz greift schneller bei thematisch klaren Topics; bei sehr breiten Fragestellungen erreicht der Hard-Cap das Ende des Loops.
- Empty-Topic (alle Tags entfernt → leerer String) wird vor dem ersten LLM-Call abgefangen (
error_code="empty_topic").
Best Practice: Full Dev-Loop Workflow
A battle-tested 8-step queue pattern for implementing a plan end-to-end with cost-optimized model tiering. Strong models (Opus) handle value creation and final validation; cheaper tiers (codex_mini, codex) do the iterative cleanup; Codex runs strictly read-only as a second opinion.
Recommendation: keep plans small (one feature / one phase per plan file) and apply this flow per plan. For multi-phase changes, split the plan file into several smaller ones — one commit per plan is cleaner than one commit for many phases.
- [ ] Implement docs\plan-XXX.md. dont commit the changes! #id:ID1 #tool:dev-loop #claude_opus cwd:<repo>
- [ ] security-audit of the uncommitted changes. dont commit the changes! #id:ID2 #need:ID1 #tool:security-audit #claude_opus cwd:<repo>
- [ ] use your simplify skill for the uncommitted changes. dont commit the changes! #id:ID3 #need:ID2 #claude_sonnet cwd:<repo>
- [ ] Review-fix loop for the uncommitted changes. dont commit the changes! #tool:review-loop #id:ID4 #need:ID3 #codex_mini cwd:<repo>
- [ ] Review-fix loop for the uncommitted changes. dont commit the changes! #tool:review-loop #id:ID5 #need:ID4 #codex cwd:<repo>
- [ ] Critical review (read-only) of the uncommitted changes against docs\plan-XXX.md #tool:critical-review #pass1:claude #pass2:codex #id:ID6 #need:ID5 cwd:<repo>
- [ ] Review-fix loop for the uncommitted changes. Also incorporate findings from the most recent critical-review report in docs/. dont commit the changes! #tool:review-loop #id:ID7 #need:ID6 #claude_opus cwd:<repo>
- [ ] 1. check the uncommitted changes. 2. update all docs in the repo and the Obsidian Project. 3. commit it. #need:ID7 #claude_haiku cwd:<repo>
Why this tiering
| Step | Model | Rationale |
|---|---|---|
| 1. dev-loop | #claude_opus |
Core value creation; bad code here inflates every downstream step |
| 2. security-audit | #claude_opus |
Finds subtle exploit chains; cheaper tiers miss logic flaws |
| 3. simplify | #claude_sonnet |
Refactoring is a bounded task |
| 4. review-loop (pass A) | #codex_mini |
Cheap first pass — obvious bugs, unused imports, trivial wins |
| 5. review-loop (pass B) | #codex (CLI default) |
Mid-tier — structural issues, missing coverage |
| 6. critical-review | #pass1:claude + #pass2:codex |
Independent second opinion, strictly read-only — zero risk of broken code |
| 7. review-loop (final) | #claude_opus |
Final validator; integrates critical-review findings. If Opus finds nothing here, the code is genuinely clean |
| 8. commit | #claude_haiku |
Trivial — diff + doc sync + single commit. Escalate to #claude_sonnet if the plan spans multiple commits |
Variants
- Minimal (trivial changes): dev-loop → review-loop
#codex_mini→ review-loop#claude_opus→ commit#claude_haiku - Security-critical: swap step 2 for
#tool:deep-security-audit(6-agent deep scan) - Multi-commit plans: raise step 8 to
#claude_sonnetand instruct it to split viagit add -p
External second opinion: Codex first, Mistral Vibe for high-risk work
Step 6 (#tool:critical-review #pass2:codex) runs Codex as an independent, read-only second opinion — a non-Claude voice catches assumptions the Claude reviewers share. Codex is the default external reviewer.
For high-risk changes a second non-Claude voice is available: #second_opinion:vibe (or :vibe_medium / :vibe_small) on a #tool:review-loop task routes to the Mistral Vibe CLI, which runs strictly read-only — all tools disabled, or read_file+grep at most. It is opt-in, pay-per-token (VIBE_MAX_PRICE_USD caps a run), and never joins the fallback chain. Reserve it for cases where one external opinion is not enough; two paid reviewers on trivial diffs is waste.
Gemini is no longer used as a reviewer: the consumer CLI was retired (2026-06-18) and data-training/privacy concerns rule out the HTTP mode for review content. Gemini remains available only as a fallback execution provider in the routing chain, never as a reviewer in dev-loop/review-loop/critical-review.
Skills (SKILL.md)
In addition to built-in tools, skills can be discovered from SKILL.md files.
Search order (higher priority overrides lower):
<cwd>/.orchestrator/skills/<name>/SKILL.md./skills/<name>/SKILL.md<vault>/99_System/AI/Skills/<name>/SKILL.md./tools/<name>/SKILL.md
Skills can define requirements (binaries, env vars, OS, provider). Skills whose requirements are not met are gated rather than silently skipped.
Execution Profiles (#agent:<name>)
Profiles are YAML files that bundle execution rules per task type.
Typical contents:
- Provider order
- Allowed / denied skills
- Timeout override
- Safety / sandbox level
- Profile-specific policy rules (
auto/approve/deny)
Search locations:
<vault>/99_System/AI/profiles/<name>.yaml./profiles/<name>.yaml
Execution Policy & Approvals
The policy classifies tasks as:
AUTO→ runs without confirmationAPPROVE→ requires Telegram approvalDENY→ task is blocked
Policy file: <vault>/99_System/AI/policy.yaml
Telegram approval commands: /approve, /approve-all <category>, /deny, /skip
Tasks can also carry preapprovals: #approve:push,publish
Telegram Control
In --watch mode a Telegram long-poll listener starts (when TELEGRAM_* env vars are set).
| Command | Description |
|---|---|
/task <text> |
Add free-form task to queue |
/review [cwd] |
Queue #tool:review-loop task for cwd (or last-cwd) |
/security [cwd] |
Queue #tool:security-audit task |
/audit [cwd] |
Queue #tool:deep-security-audit task |
/dev <desc> cwd:<path> |
Queue #tool:dev-loop task |
/critique <plan.md> |
Queue #tool:critical-review task (cwd = parent dir of plan) |
/brainstorm <topic> |
Queue #tool:brainstorm task (uses last-cwd) |
/status |
Queue size + provider status |
/limits |
Detailed limits with per-window breakdown |
/pause / /resume |
Pause / resume processing |
/approve, /approve-all <cat>, /deny, /skip |
Approval flow |
/reject <run_id> [criterion] [reason] |
Reject a scientific-investigation pre-registration / final gate |
/unblock <id>, /drop <id>, /retry <dep-id> |
Queue-healing responses for long-blocked tasks |
/pr-fix <owner/repo#N>, /pr-ignore <owner/repo#N> |
PR-Babysitter report-only mode: queue a fix task or silence the PR |
/pick N |
Accept usage suggestion (1–3) |
/decline |
Decline suggestions |
/cancel-shutdown |
Cancel pending shutdown |
/help |
Show available commands |
Plain text → AI chat (answered by best available provider).#shutdown as standalone tag → schedule shutdown.
Slash tool-commands (/review, /security, /audit, /dev, /critique, /brainstorm) expand to a queue line with the right #tool: tag and run through the full pipeline (provider routing, policy, memory, approvals). Each chat has a RAM-only last-cwd memory: after one explicit /review D:\foo, subsequent commands without cwd: reuse that path. CWDs are validated against ALLOWED_CWD_ROOTS before any task is queued, and duplicate deliveries of the same Telegram message are deduplicated via idempotency.py.
Rate limits (anti-spam):
| Category | Limit |
|---|---|
| Commands | 20/min |
| AI chats | 5/min |
| Task adds | 10/min |
Memory, Heartbeat, SOUL.md
Memory (
memory.py) — Four-layer architecture, ordered for max prompt-cache reuse:- Curated (
MEMORY.md): Long-term patterns, conventions, decisions. Always in prompt. (Most static.) - Lessons Learned (
lessons.md): LLM-summarized patterns from multi-iteration tool loops. CWD-filtered injection (universal*entries always, project-specific only when CWD matches). Semantic dedup via TF-IDF similarity at write time. (Stable per tool+cwd.) - Daily Logs (
daily/): Append-only log for today + yesterday (temporal locality). (Grows during the day — placed AFTER lessons so daily growth doesn't break the cache prefix for tool reruns.) - TF-IDF Deep Search (
task_results/): Keyword matching + temporal decay over all past tasks. (Most volatile — task-specific.)
- Top-K relevant memories are intelligently injected into the prompt.
- Auto-archival after 180 days.
- Curated (
Heartbeat (
heartbeat.py) — Proactive checks in--watchmode, configured via<vault>/99_System/AI/HEARTBEAT.md.- 14 built-in handlers:
queue-idle,queue-healing,git-status,disk-space,check-limits,log-capacity,summarize,stale-branch,usage-suggest,session-cleanup,model-check,skill-suggest,status-recap,check-ci-failures - Sections support
## Every N minutes/hours/daysand## Daily (after HH:MM)— so a monthly check is just## Every 30 days. model-check(recommended monthly): CLI-probes every entry inCLAUDE_MODEL_ALIASES/GEMINI_MODEL_ALIASES/CODEX_MODEL_ALIASESto detect dead IDs (skips providers currently in cooldown; pay-per-token aliases are not probed — a scheduled ping would be a recurring charge), then asks the best available LLM with today's date as anchor whether newer IDs are known. That second phase lists all five alias dicts includingor_*andvibe_*, since one call costs the same regardless of list length. Telegram notification only fires on findings; LLM-call failures surface as⚠️ LLM-Check failed: …instead of being silently swallowed.- Persistent state: items with interval ≥ 1 day record their last run in
logs/heartbeat-state.json, so a## Every 30 dayscheck does NOT fire on every--watchrestart. session-cleanupdeletes orchestrator-created Claude session JSONL files in~/.claude/projects/**older thanORCH_SESSION_RETENTION_DAYS— uses sidecar whitelist (logs/orchestrator-sessions.jsonl) to NEVER touch interactive Claude Code sessions.- Mtime-cached config — changes to
HEARTBEAT.mdtake effect immediately (no restart). - Runs in a daemon thread (60s poll) so scheduled checks fire on time even during long-running tasks.
- 14 built-in handlers:
Usage Suggester (
usage_suggester.py) — Detects when Claude limits are about to reset with capacity still available. Proactively suggests 2–3 tasks via Telegram (skills, git changes, failed retries, vault tasks). Answer with/pick Nor/decline.SOUL.md — Central prompt/personality definition at
<vault>/99_System/AI/SOUL.md. Supports provider-specific sections (### claude,### gemini,### codex). Mtime-cached — changes take effect on the next task.
Analytics Dashboard
# Start dashboard (opens browser automatically)
python orchestrator.py --dashboard
# Standalone with options
python dashboard.py
python dashboard.py --port 9000
python dashboard.py --no-open
Dashboard sections:
- Summary cards: total tasks, success rate, avg duration, active providers
- Tasks/day (30 days): bar chart of daily throughput
- Provider distribution: donut chart of usage per provider
- Provider capacity (48h / 7d / 30d): three timeline charts
- Recent events: error lines from logs + queue events
- Session stats: live data for the current
--watchsession - Billing analytics: weighted token cost (
input × 1.0 + cache_creation × 1.25 + cache_read × 0.1 + output × 5.0) and cache hit rate from Claude prompt cache. Quota gating uses ONLYinput + output— cache fields are billing-only.
Default port: 8211 (configurable via DASHBOARD_PORT). If the port is already in use or reserved by Windows (Hyper-V/WSL dynamic ranges → WinError 10013), the server automatically falls back to a free port and logs the actual URL.
Quota Calibration & State (cclimits anchor → local estimation)
cclimits reads Claude/Codex/Gemini quota from the undocumented, aggressively rate-limited api.anthropic.com/api/oauth/usage endpoint (anthropics/claude-code#31637). To depend on it less, the orchestrator calibrates a local tokens_per_pct model against real cclimits readings and persists a single-source-of-truth state file:
- Phase 0 — telemetry (
quota_calibration.py): each successfulcclimitspoll appends a CSV row per Claude window (5h / 7d) tologs/quota-calibration.csv, pairing the real utilization-% with locally-aggregated JSONL token counts. Calibration selected theio_onlymodel (input + output; cache tokens are negligible to the rate limit). - Phase 1 — SoTH + estimation (
quota_state.py):_bg_refresh_loopwriteslogs/cc_quota_state.jsonatomically each poll (per-windowremaining_pct/ reset times + embedded calibration constants). The Claude Code statusline and--check-limitsread it instead of re-polling cclimits; the 429-fallback estimator uses the calibrated per-window factors. - Phase 2 — live rebalancing (opt-in,
ORCH_QUOTA_LIVE_ESTIMATE, default off): between polls,get_limits()decrements the cached snapshot by the calibrated per-task estimate and re-anchors on each fresh cclimits poll; optional daily auto-recalibration of the factors from the running CSV (ORCH_QUOTA_AUTO_RECALIBRATE, min-samples + clamp guarded). Default off keeps cclimits the per-poll source of truth.
The calibrated factors are plan- and workload-specific (env-overridable, not universal constants). Full CSV schema, calibration result, limitations, and the refuted 1h/5m tier-split experiment → docs/architecture/components.md.
Security / Guardrails
- Hard bans on destructive commands (
rm -rf,git reset --hard, force-push,DROP TABLE, etc.) - File deletion limits
- Protection against changes outside
cwd(unless explicitly requested) cwd:validation againstALLOWED_CWD_ROOTS(when set)- File-change snapshot + change summary after each task
Prompt Budget (Token Allocation)
| Component | Budget | Source |
|---|---|---|
| Core (task + safety) | ~200 tokens | config.py / SOUL.md |
| Curated Memory (L1) | ~500 tokens | MEMORY.md |
| Lessons (L2) | ~1000 tokens | lessons.md (cwd-filtered) |
| Daily Log (L3) | ~500 tokens | daily/ |
| TF-IDF Memory (L4) | ~2000 tokens | memory.py |
| Wikilink context | ~1500 tokens | queue_manager.py |
| Skill prompt | ~2000 tokens | SKILL.md body (only with #tool:) |
| Total | ~7 500 tokens | (under PROMPT_BUDGET_TOKENS=10000) |
Doctor (--doctor)
python orchestrator.py --doctor runs 18 checks:
- Provider CLIs (
claude,gemini,codex;vibereported as WARN when absent — it is optional) - Worktrees (orphaned
.worktrees/parallel-*dirs,--fixprunes them) git,cclimits- Vault path + queue file
- Telegram bot configuration (
getMeAPI call) .env(present + required keys)- Skills discovery + requirements gating
- Memory directory
- Heartbeat file
- Profiles directory + validation
- Policy file
- Provider limits (via
cclimits) - Model IDs — CLI-pings every entry in
CLAUDE/GEMINI/CODEX_MODEL_ALIASESconcurrently (~5–10 s). FAIL on rejected IDs (unknown/deprecated), WARN on transient probe errors, PASS when every alias responds live. The pay-per-token aliases (or_*,vibe_*) are deliberately not probed — a probe would cost money on every--doctorrun. The deeper LLM-based "are there newer IDs?" heuristic runs only in the monthly heartbeatmodel-check, not here.
With --fix (optionally --yes) simple problems are auto-created/repaired.
Architecture
orchestrator.py
→ dispatcher.py (provider selection + fallback)
→ providers/ claude, gemini (HTTP or CLI), codex ← fallback chain
openrouter, vibe ← opt-in only, tag-gated
→ queue_manager.py (queue read/write, tags, atomic updates)
→ parallel_runner.py (#parallel subtasks)
→ tools/registry.py (#tool handlers)
→ skills/* (SKILL.md discovery / gating / loader)
→ policy.py (AUTO/APPROVE/DENY + Telegram approval)
→ profiles.py (#agent profiles)
→ memory.py (context store)
→ heartbeat.py (watch-mode checks)
→ usage_suggester.py (proactive suggestions on free capacity)
→ analytics.py (data parsing + aggregation for dashboard)
→ dashboard.py (HTTP server + Chart.js dashboard)
→ telegram_listener.py (Telegram commands + chat)
→ notifier.py (Telegram notifications)
→ shutdown.py (shutdown countdown / cancel)
→ limits.py (cclimits wrapper, disk cache, 429 resilience)
→ quota_calibration.py (Phase-0 telemetry: cclimits ↔ JSONL token pairs)
→ quota_state.py (Phase-1 SoTH: logs/cc_quota_state.json for statusline)
→ logging_setup.py (rotating file logger)
→ doctor.py (setup validation / --doctor)
→ queue_linter.py (offline queue validation / --lint-queue)
→ idempotency.py (duplicate-trigger dedup for external sources)
→ replay.py (logs/runs.jsonl run records)
→ taxonomy.py (19-category failure classification over runs.jsonl)
→ preflight.py (deterministic per-tool context collection)
→ queue_healing.py (long-blocked task detection + /unblock /drop /retry)
→ skill_suggester.py (draft-only SKILL.md proposals, pattern-gated)
→ session_registry.py (whitelist of orchestrator-created Claude sessions)
→ usage_budget.py (pace analysis over rolling windows)
→ ci_watcher.py (CI-failure sweep) → gh_helpers.py (gh CLI wrapper)
→ config.py (constants, .env loader, SOUL.md loader)
Troubleshooting
- Run
--doctorfirst - Run
--check-limitsif no providers are being used - For
cwd:errors: verify the path and setALLOWED_CWD_ROOTSin.envif needed - For Telegram issues: check
TELEGRAM_BOT_TOKENandTELEGRAM_CHAT_ID - Logs:
logs/orchestrator.log
Testing
# Run all tests (~1723 tests, ~90 s)
python -m pytest tests/ -q
# Run a single test file
python -m pytest tests/test_parallel_runner.py -v
Contributing
PRs welcome. Run python -m pytest tests/ -q before submitting. All tests must pass.
License
MIT — see LICENSE.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found