# Model Orchestrator > Model router for AI coding agents: routing rules, subagents and a CLI runner that give your AI a playbook for the model, effort and tools each task needs. - [Product page](https://model-orchestrator.aunysillyme.dev/) - [Installation guide](https://model-orchestrator.aunysillyme.dev/docs/install/) - [How routing works](https://model-orchestrator.aunysillyme.dev/docs/how-it-routes/) - [Commands](https://model-orchestrator.aunysillyme.dev/docs/commands/) - [GitHub](https://github.com/aunysillyme/model-orchestrator) - [npm](https://www.npmjs.com/package/model-orchestrator) ## Public documentation - [model-orchestrator](https://model-orchestrator.aunysillyme.dev/docs/overview/index.md) - [Documentation](https://model-orchestrator.aunysillyme.dev/docs/guides/index.md) - [Commands and lane runner](https://model-orchestrator.aunysillyme.dev/docs/commands/index.md) - [Reproduce the measurements](https://model-orchestrator.aunysillyme.dev/docs/proof/index.md) - [Changelog](https://model-orchestrator.aunysillyme.dev/docs/changelog/index.md) - [Installing](https://model-orchestrator.aunysillyme.dev/docs/install/index.md) - [How the model router chooses work](https://model-orchestrator.aunysillyme.dev/docs/how-it-routes/index.md) - [Guarantees and verification](https://model-orchestrator.aunysillyme.dev/docs/guarantees/index.md) - [Works well with](https://model-orchestrator.aunysillyme.dev/docs/companions/index.md) - [Catalog](https://model-orchestrator.aunysillyme.dev/docs/catalog/index.md) - [Part 1: route work inside one agent](https://model-orchestrator.aunysillyme.dev/docs/part-1-beginner/index.md) - [Part 2: delegate across your AI CLIs](https://model-orchestrator.aunysillyme.dev/docs/part-2-intermediate/index.md) - [Part 3 · Advanced: templates for your always-on Linux machine](https://model-orchestrator.aunysillyme.dev/docs/part-3-advanced/index.md) - [Security review history](https://model-orchestrator.aunysillyme.dev/docs/security-review-history/index.md) --- Canonical page: https://model-orchestrator.aunysillyme.dev/docs/overview/ Source: https://github.com/aunysillyme/model-orchestrator/blob/205760296bb61082960552986181bfb3b9fa6196/README.md Relative links in the source below resolve against that repository source location. --- # model-orchestrator [![npm](https://img.shields.io/npm/v/model-orchestrator.svg)](https://www.npmjs.com/package/model-orchestrator) [![test](https://github.com/aunysillyme/model-orchestrator/actions/workflows/test.yml/badge.svg)](https://github.com/aunysillyme/model-orchestrator/actions/workflows/test.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![node >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](package.json) **Model router for AI coding agents: routes routine work to cheaper models with routing rules, 8 subagents, hooks and a CLI runner.** Routine work can use a cheap model. Planning and difficult decisions can use a stronger one. Your agent gets editable rules for making that choice and a runner that checks whether delegated work returned a result that passes your checks. A **lane** is one AI tool or model your agent can hand work to. A **tier** describes a model's strength and cost: planning model, working model or cheap model. ```bash npx model-orchestrator ``` ![Tasks routed to the right model and effort](docs/router-trailer.gif) ## What the model router gives you | Part | What you get | |---|---| | Routing rules | A decision tree for bulk work, reading, live data, review, verification, planning and builds | | Subagents and hooks | Named jobs with explicit tools, plus routing reminders inside Claude Code | | Step-by-step playbooks (protocols) | A build process from acceptance checks through independent review and verified use | | Task brief and context file | One shared set of facts, plus each worker's scope, permissions and checks | | Lane runner | Honest exit codes, optional file or JSON checks, requested model and effort recorded per run, verify commands that fail the run (exit 24), and a switch to another installed lane when one hits its quota | | Routing metrics | Your local routing split and subagent activity | The package sits **above the request layer**: your agent reads the rules and picks the lane. Request-level proxies and gateways can carry the API calls underneath it. `aunx route` prints a deterministic suggestion for your agent to consider. **For agents:** [llms.txt](llms.txt) links the reference docs; [AGENTS.md](AGENTS.md) gives headless commands. Preview a setup: ```bash npx model-orchestrator --yes --level 2 --ais claude-code,codex --primary claude-code --project . --dir ./ai-orchestrator --dry ``` ## After you install The interactive installer applies the main agent's project rules and, for Claude Code, merges its hooks. The summary names these changes before the single confirmation; existing files get timestamped backups in `/.backups/`. That directory is gitignored and retained by uninstall, which stores its backups there too. A local health check runs automatically after installation. - **What's left for you:** follow the remaining sign-in or setup steps printed at the end. Codex sign-in is listed only when its reliable status check cannot confirm it; other CLIs get an "if you have not signed in yet" instruction. A chat-app main agent keeps one paste step. A CLI main agent with no cataloged project rules file (for example Grok or Hermes) gets a "load the block" instruction instead of a file to copy. - **Your agent:** start a fresh session in the project. `ai-orchestrator/README.md` (or the README in your chosen `--dir`) explains the installed rules and activation check. - **Control:** use `--no-apply` or the edit screen to keep activation manual. Headless `--yes` keeps project rules and settings untouched unless you add `--apply-snippets`. Use `--dry` to preview. Selected companions get project-scoped registration where the host supports it; global configuration remains a printed step. The health check checks CLI presence and sends no prompt. Live checks remain opt-in with `aunx cli-run --doctor --run`. [Installation and upgrades](docs/install.md) cover every flag. Install the command once to use the shorter forms below: ```bash npm install -g model-orchestrator aunx --help ``` `aunx` without a subcommand runs the same installer as `model-orchestrator`. The installer writes its files and the project activation changes shown in the summary. Every companion is opt-in, including with `--yes`; missing tools appear together under **Install these yourself**, with official commands and links. ## Part of a set | Repo | What it gives you | |---|---| | [agent-personalizer](https://github.com/aunysillyme/agent-personalizer) | AI custom instructions from approved entries: a profile and rules for Claude Code, Codex, Cursor, Gemini CLI and ChatGPT, kept in sync from one source. | | **model-orchestrator** | Model router for AI coding agents: routes routine work to cheaper models with routing rules, 8 subagents, hooks and a CLI runner | | [repo-build-skill](https://github.com/aunysillyme/repo-build-skill) | Skill pack that teaches your AI to build, release and launch a GitHub repo: stack pick, README, AGENTS.md, llms.txt, repo SEO and AEO, trusted publishing. | | [website-build-skill](https://github.com/aunysillyme/website-build-skill) | A skill pack that turns your AI into an expert website builder: current research, design, code, accessibility, speed, SEO and security, alone or as a team. | ## Model routing and request-level proxies An HTTP proxy or gateway such as LiteLLM, Portkey, OpenRouter or claude-code-router swaps the model per request underneath the agent. model-orchestrator is an installer that writes routing rules, subagents, hooks and a lane runner above the request layer. - **Pick a proxy** for request-level model routing and a shared API entry point. - **Pick model-orchestrator** for task delegation across your agents, model tiers and CLIs. They compose: your agent follows the installed rules, and a proxy can route its API requests underneath. ## Choose the setup that fits your tools | Level | Your setup | What it adds | |---|---|---| | Beginner | One agent or chat app | Task classification, model tiers, a task brief, acceptance checks and build protocols | | Intermediate | Several AI CLIs | A lane runner, delegation matrix, research triage and model/effort flags | | Advanced | An always-on Linux machine | Gateway templates, privacy rules and a scheduled review job | Read [beginner](docs/part-1-beginner.md), [intermediate](docs/part-2-intermediate.md) or [advanced](docs/part-3-advanced.md). ## Works with the AIs you already pay for The installer detects your AI tools, shows the proposed setup and asks **Write these files?** with `[Y/n/e]`. Confirm once, or enter `e` to change a setting. With no detected tools, select the AIs you have first. The main agent receives its supported agent set; at level 2 and up, selected CLIs supported by the runner become worker lanes. | AI | Installer ID | What it is and gives | |---|---|---| | Claude Code | `claude-code` | Anthropic's terminal agent; project rules, subagents and routing hooks when main | | Codex | `codex` | OpenAI's terminal agent; read-only filesystem sandbox for `--audit`, project rules when main | | Antigravity | `agy` | Google's terminal agent; project rules and custom agents when main | | Grok | `grok` | xAI's terminal agent on a subscription | | Hermes | `hermes` | A free terminal agent using the providers you authenticate | | Qwen Code | `qwen` | A terminal agent using your provider key; project rules when main | | Ollama | `ollama` | A model runtime that runs on your machine | | Claude, ChatGPT and Gemini apps | `claude-app`, `chatgpt-app`, `gemini-app` | `PASTE-INTO-YOUR-AGENT.md`: a routing block for your chat app | Every install includes **Your stack: who does what**: planning, building, independent review, verification, research, bulk work, reading and private work, plus fan-out and long-context roles when supported. One deterministic assignment uses the selected tools' capabilities, billing and selection order. Independent review requires a known different model family; private work requires a local runtime. Unavailable roles are stated explicitly. Subagent definitions name planning, working or cheap model tiers. Your plan and tool configuration select the actual models. `MANIFEST.json` stores the assignment, and `aunx route` reads it even when you keep edited routing documents. [How assignment works](docs/how-it-routes.md). `npx model-orchestrator --list` prints supported IDs and setup notes. [The catalog](docs/catalog.md) lists installation, sign-in and detection details. ## Lane runner (`aunx cli-run`) Run from your project. `aunx cli-run` uses the package runner; pass `--dir` to use a project's installed runner. Replace `` below with a CLI lane from your generated stack table. ```bash aunx cli-run --doctor aunx cli-run '' --brief TASK_BRIEF.md # Direct form from the installed rules folder: node bin/cli-run.mjs '' --brief TASK_BRIEF.md ``` `--doctor --run` sends a small live check through your own vendor sign-ins. Each run records the requested model and effort and a fixed result class in a local log. A missing result returns nonzero. Add `--expect-file` or `--expect-json` when success needs a concrete output contract. Verify commands from a brief's `## Verify commands` section or repeatable `--verify` run after accepted output; a failure exits 24 and keeps the output. After a quota result, `fallbacks.json` guides the runner to the next installed lane in another billing pool and names the lane that ran; `--no-fallback` turns this off. [Runner reference](bin/README.md). ## Task brief (`aunx brief`) and acceptance checks (`aunx checks`) ```bash aunx context CONTEXT.md aunx brief new TASK_BRIEF.md aunx checks ACCEPTANCE_CHECKS.json aunx checks run ACCEPTANCE_CHECKS.json ``` Fill the context file with verified facts, quote the user's ask in the brief, and give every requirement a check command. The check runner reports PASS or FAIL and exits 1 when a check fails. Run only check files you trust: their commands execute with your shell's permissions. See [the acceptance-check protocol](templates/common/protocols/acceptance-checks.md). ## Routing suggestions (`aunx route`) ```bash aunx route "rename this file" aunx route "design the auth system" aunx route --class frontier --data private --write ``` The first suggests cheap bulk work; the second suggests planning. Each prints the AI assigned to that role in your installation. Use `--dir PATH` for a custom rules folder; otherwise it reads `./ai-orchestrator/MANIFEST.json`, then `./MANIFEST.json`. With no install, it prints a generic suggestion and an install notice. The classifier uses keywords and points unmatched requests to `ROUTING.md`. It reads JSON and launches no worker. The class command prints the first installed lane eligible for that class, data and read or write access, and names why each earlier candidate was skipped. Classes live in editable `classes.json`; `--json` gives machine output. ## See where your agent sends work (`aunx route-metrics`) ```bash aunx route-metrics --summary aunx route-metrics --summary --since 2026-09-01 ``` On Claude Code installs, the routing hook records turns, route markers and subagent activity locally. The summary reports your routing split, valid route-marker coverage, invalid marker count and agent durations. SubagentStop events also record totalTokens, numeric usage counters and model identifiers when the hook payload provides them; the summary totals reported tokens. Older payloads keep those fields absent. Prompt text and the explanation inside a route marker never enter that log. [Measured results and reproduction scripts](proof/README.md) show dated figures with methods and sample sizes. An expired entry is a signal to re-measure; the test suite rejects future-dated or incomplete entries. ## Claude Code plugin ```text /plugin marketplace add aunysillyme/model-orchestrator /plugin install model-orchestrator@model-orchestrator ``` The plugin carries read-only routing hooks and the subagents. Generate your project's routing rules with `npx model-orchestrator`. The npm installer also supplies the local metrics hook. On Claude Code installs, every prompt carries the ~1.7 KB route table as context; include that overhead when comparing token use. [Plugin setup](plugin/README.md). ## Skill packs on Agensi The beginner and intermediate setups are also standalone skill packs on Agensi, a marketplace for agent skills. Each pack is a SKILL.md your AI follows to set itself up, built from the same templates as the installer. No npm needed. | Pack | What you get | |---|---| | [Beginner](https://www.agensi.io/skills/model-router-beginner-right-model-for-every-task) | Routing rules, task briefs and protocols for one main agent | | [Intermediate](https://www.agensi.io/skills/model-router-intermediate-delegate-across-your-ai-clis) | Adds the CLI runner and lanes file for worker AIs | `npx model-orchestrator` is free and adds project activation, hooks, health check and uninstall. ## Works well with These are other authors' projects, maintained in their own repositories. All companions start unselected. Choosing one writes guidance and configuration snippets. With activation enabled, supported project configuration is merged automatically; you install the tool and complete any printed setup steps yourself. | Project | Author | What it adds | |---|---|---| | [codecalc](https://github.com/The-40-Thieves/codecalc) | The-40-Thieves | Local calculation, code execution and logic checks | | [obsidian-tc](https://github.com/The-40-Thieves/obsidian-tc) | The-40-Thieves | Searchable notes and controlled writes over an Obsidian vault | | [Context7](https://github.com/upstash/context7) | Upstash | Current, version-specific library documentation | Use `--tools codecalc,obsidian-tc,context7` to select them. Without a companion, use your available calculator or runtime, a searchable notes folder and official library docs. [Companion setup and upstream support](docs/companions.md). ## Common questions ### What is a model router for coding agents? It helps an agent match a task to a model, effort level and toolset. Run `npx model-orchestrator` to install editable rules, subagents and a CLI runner for your setup; your agent makes the routing decision. ### How do I use Claude Code and Codex together? Run `npx model-orchestrator --yes --level 2 --ais claude-code,codex --primary claude-code --project . --dir ./ai-orchestrator`, then follow the activation summary. Claude Code can dispatch scoped work through `aunx cli-run codex --brief TASK_BRIEF.md` and use a different model family for review. For Codex as the main agent, set `--primary codex`. Independent review goes to Claude Code through a connected Claude worker MCP service when the host has one, or through `aunx cli-run claude --brief TASK_BRIEF.md`. The `claude` lane runs in `dontAsk` mode, so calls needing approval are denied; `--audit` stays Codex-only. ### How do I reduce Claude Code token usage? Install routing rules with `npx model-orchestrator`, so your agent has guidance for sending routine work to cheaper models and keeping reads scoped. Use `aunx route-metrics --summary` to measure where your work goes; savings depend on your tasks and model choices. ### How do I route tasks to cheaper models? Use `aunx route "rename this file"` for a keyword-based suggestion, then apply your installed `ORCHESTRATOR.md` at level 1, or `ROUTING.md` and `TIERS.md` at level 2 and up, to the actual task. Role selects the job, complexity sets effort, and the consequences of a mistake affect the model and reviewer. ### How does this work with an AI gateway or LLM router? It coordinates multi-agent work at task level; a proxy such as LiteLLM or OpenRouter can route the API requests underneath it. `npx model-orchestrator --level 3` includes gateway templates when you want that setup. ### How does an agent install and run it headlessly? Pass `--yes --level 2 --ais claude-code,codex --project . --dir ./ai-orchestrator` to `npx model-orchestrator`; add `--dry-run` to preview. Existing edits are preserved by default, and `--update-docs` refreshes files whose installed hashes still match. ## Uninstall Run `npx model-orchestrator --uninstall --dir ./ai-orchestrator --project .` (add `--dry` to preview). The installer removes unedited managed files, its recorded activation block and the hook entries it added, preserving surrounding rules and settings. It names edited or manually pasted entries that need your attention; backups stay. [Removal details](docs/install.md#uninstall). ## Read next - [Install and upgrade](docs/install.md): flags, folders, walkthrough and safe reruns. - [How routing works](docs/how-it-routes.md): model, effort and independent verification. - [Guarantees](docs/guarantees.md): executable checks and agent instructions. - [Proof](proof/README.md): dated measurements and scripts you can rerun. - [Security review history](docs/security-review-history.md): findings, fixes and regression evidence.
Platform support, and every test this suite skips Node 18 or newer, with zero runtime dependencies. Works on macOS and Linux; the level 3 box templates assume Ubuntu. Windows: CI runs the suite on `windows-latest` (Node 18, 20, 22), including lane execution end to end through `cli-run` against a fake CLI installed the same way npm installs a real one (a `.cmd` shim). `cli-run` never runs a lane through `cmd.exe`: it resolves the shim to the Node script underneath and spawns Node directly, so a prompt reaching a real lane never passes through a Windows shell. A `.cmd` or `.bat` lane that cannot be resolved that way (an old or hand-edited shim) is refused with exit 13 and a message saying how to fix it, rather than run through `cmd.exe`: a batch file re-reads its arguments after `cmd.exe` has parsed them once, and no escaping fully contains a prompt through both passes. Install, detection, the hooks and `cli-run`'s `taskkill` tree kill are tested on Windows too, including SIGTERM/SIGINT to the wrapper (Windows has no OS-level signals: both terminate it unconditionally, verified there rather than treated the same as POSIX). The Windows skip list covers POSIX behavior, with each skip pinned by `test/prose.test.js`: `statSync().mode`'s executable bit (NTFS has none, so that one assertion is conditional inside a test that otherwise runs everywhere); a lane dying mid-run from a real POSIX signal (a real Windows lane cannot die "by signal"); running `weekly-audit.sh`'s watchdog functions for real under Git Bash's job control, both the end-to-end run and the `bounded()` timeout check (the script itself only ever runs on the Ubuntu box it targets); and a `mkfifo` FIFO at the rules path, the one case that proves `route-gate.mjs` cannot HANG on a non-regular file, since Windows has no `mkfifo` to build one (the guard behind it is covered on every OS by a directory at the same path); and an untracked `mkfifo` FIFO in the repository `cli-run --audit` sizes, the case that proves `--effort auto` never opens a non-regular file (the symlink half of that test runs on every OS). `test/prose.test.js` counts every `skip:` in the suite and requires this list to document each one. Additional security regressions skip Windows for the project-hook symlink and manifest FIFO fixtures and the symlinked-manifest refusal message (symlink privileges and POSIX special files), the fsmonitor helper and runner lanes FIFO fixtures (POSIX executables and special files), the POSIX shell descendant timeout fixture (the argv equivalent still runs on Windows), and five weekly credential, report lifecycle and timeout marker runtime checks (the Ubuntu watchdog requires POSIX process-tree semantics). The rules-file replacement race also needs `mkfifo`. Their configuration and generated syntax remain covered on every platform. The prompt size cap regressions run the oversized-brief refusal on Linux and Windows only (macOS has no single-argument cap, so that brief runs there) and the spawn `E2BIG` fixture on Linux only (it needs a finite `ARG_MAX` to exceed); the cap arithmetic is covered on every platform. **Privacy.** The installer sends no telemetry and makes no network call of its own once it is running. Two things around that are worth being exact about: - `npx model-orchestrator` is itself a download: npm fetches this package from the registry before any of it runs. `npm install -g model-orchestrator` once, then run `model-orchestrator`, if you would rather that happen exactly one time. - Every missing vendor CLI or selected companion is listed under **Install these yourself**, with an official command and link. The installer runs no third-party installs. `--no-install` remains accepted for existing scripts. `cli-run` calls the vendor CLI you name.
Vendor version compatibility **Detection checks whether a binary is present.** Use the compatibility table below to compare vendor versions. `--doctor` reports presence; add `--run` to send a small canary to every enabled worker and check its sign-in and output against the runner's success criteria. The lane wiring and the output judges were written against these versions, which are the ones this release was exercised on: | Lane | Vendor | Version this release was built against | Where that number is proved | |---|---|---|---| | `claude` | Anthropic | 2.1.285 | `test/fixtures/claude-2.1.285-success.json`, a recorded run | | `codex` | OpenAI | 0.153.4 | `test/fixtures/codex-0.153.4.jsonl`, a recorded run | | `agy` | Google | 1.1.27 | `test/fixtures/agy-1.1.27.jsonl`, a recorded run | | `grok` | xAI | 1.0.5 | `test/fixtures/grok-1.0.5.json`, a recorded run | | `hermes` | Nous Research | 0.20.0 | `test/fixtures/hermes-0.20.0.txt`, a recorded run | | `qwen` | Alibaba | 0.22.3 | `test/fixtures/qwen-0.22.3-nokey.json`, a recorded run | | `ollama` | Ollama | 0.40.5 | the pinned image the level 3 box runs, `ollama/ollama:0.40.5` | Generated from `src/catalog.js` by `npm run gen:catalog`; `npm test` fails if this table and the catalog disagree. Original vendor fixtures were captured 2026-09-06; [fixture provenance](test/fixtures/README.md) records later captures and remaining gaps. For npm-installed lanes, `builtAgainst` in the catalog supplies both the compatibility table and the install pin. Newer vendor versions may work or may change a flag the generated wiring uses. When a lane starts failing after a vendor upgrade, compare against this table first. The `claude` lane is checked against recorded Claude Code 2.1.285 runs: a restricted success, a sign-in failure and an expired-session failure. **The live canary runs on your machine, with your credentials.** That is what `aunx cli-run --doctor --run` (direct form: `node bin/cli-run.mjs --doctor --run`) is: it sends every enabled lane one tiny prompt through your own sign-ins and reports `canary ok` or `canary FAILED rc=` per lane. Choose this optional live check after setup or a vendor upgrade when you want to verify actual responses. CI runs the full suite against stub lanes on Ubuntu, macOS and Windows, Node 18/20/22, plus a packaged install into a clean consumer. Run the live check locally to verify your own sign-ins, quota and vendor versions.
## Contributing Contributions are welcome: - **New AIs:** add an entry to `src/catalog.js`; prompts, tables, configs and docs use the catalog. - **Vendor updates:** contribute a lane fixture captured from a newer vendor version and the test that checks it. - **Docs:** fix an unclear instruction or add a reproducible example. Pull requests are accepted under the [contributor license agreement](CLA.md); the checkbox in the pull request template records your agreement. Run `npm test` with your change. Keep templates free of logic and credential values. See [CONTRIBUTING.md](CONTRIBUTING.md), [RELEASING.md](RELEASING.md) and [SECURITY.md](SECURITY.md). ## Credits - [@shawnwows](https://x.com/shawnwows) reviewed the router and made the case for separating role, complexity and stakes instead of compressing them into one scale, for recording the model and effort a lane was actually asked for, and for verifying findings before they trigger repairs. All three shipped in 0.1.14. - [@shawnbissell](https://github.com/shawnbissell) added the native Claude Code worker lane (print-mode JSON judge, failure classes, dontAsk permissions) and the connected-MCP preference for Claude workers. ## License [MIT](LICENSE) --- Canonical page: https://model-orchestrator.aunysillyme.dev/docs/guides/ Source: https://github.com/aunysillyme/model-orchestrator/blob/205760296bb61082960552986181bfb3b9fa6196/docs/README.md Relative links in the source below resolve against that repository source location. --- # Documentation Use these pages for setup, operation and evidence. The [front page](../README.md) gives the short introduction. | Page | What it gives you | |---|---| | [Install and upgrade](install.md) | One-confirm setup, edit menu, flags, project commands, safe updates and uninstall | | [How routing works](how-it-routes.md) | Stack-dependent role assignment, model tiers and independent verification | | [Guarantees](guarantees.md) | What code enforces and what the agent follows as instructions | | [Companions](companions.md) | Optional projects, setup links and alternatives using existing tools | | [Beginner](part-1-beginner.md) | Routing inside one agent or chat app | | [Intermediate](part-2-intermediate.md) | Delegation across several AI CLIs | | [Advanced](part-3-advanced.md) | Gateway templates and scheduled work on a Linux host | | [Catalog](catalog.md) | Supported tools, capability facts, unverified values, installation and sign-in notes | | [Catalog advisory checks](catalog-advisories.md) | Package and container coverage, report statuses, exceptions and manual verification | | [Security review history](security-review-history.md) | Review rounds, reproduced findings, fixes and regression tests | | [Proof](../proof/README.md) | Dated measurements, methods, sample sizes and reproduction scripts | | [Commands](../bin/README.md) | `aunx` subcommands and lane-runner exit codes | --- Canonical page: https://model-orchestrator.aunysillyme.dev/docs/commands/ Source: https://github.com/aunysillyme/model-orchestrator/blob/205760296bb61082960552986181bfb3b9fa6196/bin/README.md Relative links in the source below resolve against that repository source location. --- # Commands and lane runner Plain Node executables with zero runtime dependencies. | File | Command | What it gives you | |---|---|---| | `cli.js` | `npx model-orchestrator` | Select a setup and write routing files; print third-party setup commands for you to run | | `aunx.js` | `aunx` | Run the installer, dispatch the lane runner, scaffold briefs and checks, summarize metrics or suggest a route | | `cli-run.mjs` | `aunx cli-run` | Call an agent CLI and return nonzero when it produces no accepted result | ## Lane runner (`aunx cli-run`) ```bash aunx cli-run --doctor aunx cli-run codex --brief TASK_BRIEF.md --effort high # Direct equivalents from the installed rules folder: node bin/cli-run.mjs --doctor node bin/cli-run.mjs codex --brief TASK_BRIEF.md --effort high ``` `aunx` uses the packaged runner by default. Pass `--dir PATH` to use that project's own installed runner under `PATH/bin/`; `aunx` then prints the runner path it is using on stderr. When `--dir` names a folder with no runner, the packaged copy runs instead. Arguments and exit status pass through. `--doctor --run` performs live checks using your own vendor sign-ins and quota. | Exit | Meaning | |---|---| | 0 | Accepted result | | 2 | Invalid usage | | 10 | Empty result or unmet output contract | | 11 | No output | | 12 | Timeout | | 13 | Unavailable tool | | 14 | Authentication failure | | 15 | Quota exhausted | | 16 | Request rejected, including a prompt too large for the command line | | 17 | Required tool action refused | | 18 | Run cut short | | 24 | A verify command failed after the lane returned output; that output stays on stdout | | 130 / 143 | Interrupted by a signal on POSIX | The prompt travels as one command-line argument. When it passes the platform cap (Linux: 128 KiB in one argument; Windows: 32,767 characters of command line), or spawning fails with `E2BIG`, the runner exits 16 with reason `prompt_too_large` and does not start the lane. Save a large brief to a file and pass a short prompt that names its path; `--brief` pastes the file into the command line, so it has the same cap. The local log records requested model and effort, their source, fixed result classes and the vendor exit code. Its existing schema is preserved. It stores no prompt text or provider-supplied failure text. A verify run adds `verify`, `verify_log` and a bounded redacted `verify_output`. A quota walk adds `hops` (lane, reason, exit). Those fields are omitted when the feature did not run. Verify command output also goes to `~/.ai-orchestrator/cli-run-verify.log`. `--verify CMD` repeats. `--verify-timeout SECS` bounds each command (default 600). A brief's verify section ends at the next heading or bold field, including inline fields. Verify processes are cleaned up on normal exit, timeout and catchable interruption; POSIX interruptions exit 130 or 143. Output is redacted before retaining a bounded tail. After exit 15, `fallbacks.json` in the rules folder names the next lane in a different billing pool. Agensi packs use `rules/fallbacks.json`; `CLI_RUN_FALLBACKS` selects an explicit file. `--no-fallback` and `CLI_RUN_NO_FALLBACK=1` disable that walk. Audit runs keep exit 15 without hopping, preserving Codex's read-only sandbox and effort floor. A missing file leaves the quota result unchanged. Each attempt writes one record, including its fallback skips when a walk runs. `aunx route --class frontier|fast|swarm` resolves one installed eligible lane from `classes.json`. The regression tests for verify process cleanup on POSIX in `test/runner-audit-regressions.test.js` skip Windows: normal exit with a background sleep, SIGINT and SIGTERM require POSIX shell jobs and process-group signals. The two skip markers cover these three cases; `test/prose.test.js` requires this explanation. Stream-close output, redaction, routing and log-schema regressions run on every platform. `cli-run.mjs` exports its output judges so `test/judges.test.js` can prove each rejects the failure shapes it exists to catch. Tests use fixture output and stub CLIs. ## Acceptance-check runner (`aunx checks run`) Scaffold with `aunx checks ACCEPTANCE_CHECKS.json`, then fill each command and its expected property. `aunx checks run ACCEPTANCE_CHECKS.json` executes the commands and reports PASS or FAIL. Any failure exits 1. These are shell commands with your permissions: run only a reviewed check file. Gate a later action by running it only after exit 0. ## Briefs, context and metrics `aunx brief` prints the template; `aunx brief new TASK_BRIEF.md` writes a new file. `aunx context CONTEXT.md` scaffolds shared facts. `aunx route-metrics --summary` reports local routing activity. `aunx route "design the auth system"` prints an explained keyword-based suggestion and launches no worker. ## Operation and verification - **What and why:** one command exposes the installer and the tools an agent uses to prepare, route and verify work. - **Trigger:** a user or agent invokes a command explicitly. The wrapper registers no background service. - **Invocation chain:** npm bin link -> `bin/aunx.js` -> `src/aunx.js` -> the selected Node entry point or local scaffold/check handler. - **Dependencies:** Node as declared in `package.json`; a vendor CLI and its sign-in only when calling that vendor. Scaffolds and route suggestions work without companions. - **Reads:** package templates, an explicitly selected check file, the project's installed runner, and the existing metrics log for summaries. - **Writes:** scaffold files use exclusive creation. Check commands can write whatever their reviewed code requests. The delegated runner and metrics hook retain their documented local logs. - **Closed loop:** callers inspect the exit code and the check report. Nothing watches the wrapper as a service; the repository test workflow watches committed changes. Gate a later action on exit 0 when using acceptance checks. - **Failure modes:** invalid arguments or check format return 2, failed checks return 1, and dispatched Node commands retain their exit codes. An existing scaffold path is preserved. Without `--dir` the packaged runner always runs; `--doctor` reads `./ai-orchestrator/bin/lanes.json` when present as data only. With `--dir`, a missing project runner falls back to the packaged copy. - **Run and verify:** `node bin/aunx.js --help`, `node bin/aunx.js route "rename this file"`, and `node --test test/aunx.test.js`. For a real vendor, use the doctor command above with your own authorization and quota. - **Source of truth:** `src/aunx.js`, the templates it loads, and the dispatch and exit-code tests in `test/aunx.test.js`. --- Canonical page: https://model-orchestrator.aunysillyme.dev/docs/proof/ Source: https://github.com/aunysillyme/model-orchestrator/blob/205760296bb61082960552986181bfb3b9fa6196/proof/README.md Relative links in the source below resolve against that repository source location. --- # Reproduce the measurements Generated from [results.json](results.json). Each figure has a method, sample size, measurement date and expiry. Run the scripts on your own machine to compare. Environment: Node v22.23.3, linux x64. Timing varies with startup caches and other work on the machine. Synthetic cases show what those fixtures exercise. | Measurement | Result | Sample size | Measured | Expires | Reproduce | |---|---|---|---|---|---| | Dry install wall time | 68.44 ms median | 7 | 2026-10-05 | 2026-10-19 | [script](../proof/scripts/install-time.js) | | Lane runner overhead | 64.98 ms median difference | 7 | 2026-10-05 | 2026-10-19 | [script](../proof/scripts/runner-overhead.js) | | Empty results flagged | 10 fixtures rejected | 10 | 2026-10-05 | 2026-10-19 | [script](../proof/scripts/missing-results.js) | | Acceptance failures blocked | 4 fixtures rejected | 4 | 2026-10-05 | 2026-10-19 | [script](../proof/scripts/check-gate.js) | ## Run the proof scripts ```sh node proof/scripts/measure.js node proof/scripts/render.js npm test ``` The measurement command refreshes the reproducible entries. An expired entry is a signal to re-measure; the test suite rejects future-dated or incomplete entries and checks this page against the data. The weekly [refresh workflow](../.github/workflows/proof.yml) reruns the scripts and commits their data and generated page. ## Try the acceptance gate ```sh aunx checks ACCEPTANCE_CHECKS.json aunx checks run ACCEPTANCE_CHECKS.json ``` The scaffold starts red. Replace the sample with commands that prove your requirements, then put `aunx checks run ACCEPTANCE_CHECKS.json && ` in your own release sequence. Commands are local code you review before running. Manual evidence stays UNVERIFIED and blocks the gate. ![Acceptance gate rejects a missing output, then passes after the output exists](gate-demo.gif) The [recording script](scripts/record-gate.js) captures real command output into an asciicast, then renders it with an already installed agg. Companion tools are installed by their users. ## Measurement methods ### Dry install wall time Spawn a fresh Node installer process per sample; level 2, Claude Code + Codex, no companions, --dry. Includes Node startup and planning; writes no install files. Isolated home and PATH, no real vendors. Kind: reproducible local measurement. Sample size: 7. Measured: 2026-10-05. Expires: 2026-10-19. Source: [proof/scripts/install-time.js](../proof/scripts/install-time.js). ### Lane runner overhead Paired fresh processes: direct Node stub versus cli-run hermes with the same stub. Alternates pair order. Includes wrapper startup, validation and local log writes; excludes vendor/network/model time. Kind: reproducible local measurement. Sample size: 7. Measured: 2026-10-05. Expires: 2026-10-19. Source: [proof/scripts/runner-overhead.js](../proof/scripts/runner-overhead.js). ### Empty results flagged Run cli-run against an exit-0 stub for every supported lane, once with empty stdout and once with an empty native final result. Count exit 10/11 only. A successful Hermes response is the positive control. Synthetic fixtures measure these shapes only. Kind: reproducible local measurement. Sample size: 10. Measured: 2026-10-05. Expires: 2026-10-19. Source: [proof/scripts/missing-results.js](../proof/scripts/missing-results.js). ### Acceptance failures blocked Run aunx checks run against nonzero, manual, missing-program and timeout fixtures. Each must exit 1; a passing command must exit 0. This is a local command gate, activated by the user in their release sequence. Kind: reproducible local measurement. Sample size: 4. Measured: 2026-10-05. Expires: 2026-10-19. Source: [proof/scripts/check-gate.js](../proof/scripts/check-gate.js). ## Operation and verification - **What and why:** executable measurements keep public figures traceable to current output. - **Trigger:** weekly schedule, workflow dispatch, or `node proof/scripts/measure.js`. - **Invocation chain:** workflow -> measurement functions -> isolated Node fixtures -> results.json -> this page -> npm test. - **Dependencies:** Node and the repository. The optional GIF recorder uses agg from the asciinema project. - **Reads:** package scripts, the installer, runner and acceptance-check runner. Fixture tests use an isolated home and PATH. - **Writes:** results.json, this generated page, temporary fixture directories and local fixture logs. The recorder writes gate-demo.cast and gate-demo.gif. - **Closed loop:** the workflow fails when measurement or tests fail. GitHub Actions records the failure; repository notification settings decide who receives it. No separate alert service is configured. - **Failure modes:** runner behavior changes, missing runtime, unavailable write permission, or timing noise. Review the failed job, rerun locally, and send a reproducible issue to the repository maintainers. - **Run and verify:** run the commands above, inspect sample arrays and fixture exit codes in results.json, and require npm test to pass. - **Source of truth:** results.json and the scripts it names. --- Canonical page: https://model-orchestrator.aunysillyme.dev/docs/changelog/ Source: https://github.com/aunysillyme/model-orchestrator/blob/205760296bb61082960552986181bfb3b9fa6196/CHANGELOG.md Relative links in the source below resolve against that repository source location. --- # Changelog All notable changes to this project are documented here. The format follows [Keep a Changelog 2.0.0](https://keepachangelog.com/en/2.0.0/) and the project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html); on `0.y.z` anything may change. ## [Unreleased] ## [1.1.2] - 2026-10-22 ### Fixed - Point Claude Desktop users at `Customize > Connectors` in `templates/tools/context7/CONTEXT7.md`. Connectors moved there from `Settings > Connectors` in the current Claude desktop app and claude.ai. ## [1.1.1] - 2026-10-19 ### Changed - Render the README and llms.txt "Part of a set" sections from the shared set file. The README table has four rows, adds repo-build-skill, and each line is that package's npm description. ### Added - Check set block drift and pinned renderer and set file hashes offline in CI (AUN-1619). ## [1.1.0] - 2026-10-16 ### Added - Run brief or `--verify` commands after a lane returns output, keep that output, and exit 24 when a command fails. - Walk `fallbacks.json` after a quota result, skip exhausted billing pools and missing CLIs, and name the lane that ran. - Resolve `frontier`, `fast` and `swarm` with `aunx route --class`, using one shared eligibility block for public or private data and for read or write. ### Fixed - End brief verify sections at any heading or bold field, clean up verify descendants on exit and interruption, wait for stream close, and redact output before clipping. - Keep audit quota results read-only by returning exit 15 without fallback; discover fallback files in both installer and Agensi layouts. - Write one complete record per fallback attempt, exclude unknown lanes from hops, and escape terminal controls in fallback and verify diagnostics. ## [1.0.17] - 2026-10-16 ### Changed - Add Biome lint, non-strict JavaScript type checks and a 160-character source line cap to CI; split the installer into modules behind its existing public API (#72). ## [1.0.16] - 2026-10-16 ### Security - Pin the Level 3 images to `ghcr.io/berriai/litellm:v1.105.1` (from v1.104.0, clears the multidict advisory) and `ollama/ollama:0.40.5` (from 0.35.1, 139 findings to 122), the newest stable releases. No published image, release candidates included, clears the rest: litellm still ships glibc 2.44-r6 and ollama still compiles in older Go modules. The remaining findings are dated exceptions that expire on 2026-11-15 (AUN-1552). Ollama 0.40.2+ upgrades previously downloaded models in the background on first run and keeps the original copy as a backup, so the `ollama` volume temporarily grows. ## [1.0.15] - 2026-10-16 ### Fixed - Raise `aunx route` to the builder at xhigh effort for production, auth, payment, secret, migration and schema work, even when the wording sounds mechanical; risky questions with no change verb stay unrouted (#70). - Check the prompt against each platform's command-line limit before starting a lane, and report an oversized prompt or a spawn `E2BIG` as `prompt_too_large` with exit 16 instead of a missing CLI with exit 13 (#71). - Enforce installed project lane data for ordinary packaged runner calls, refusing malformed or unsafe configuration without executing project code (#63). - Exclude missing and invalid route markers from coverage, delegated-work counts and reconciliation, and report invalid-marker counts (#64). - Put the Codex prompt after the option terminator so option-shaped text remains prompt data (#65). - Record exact inserted rule separators, restore CRLF files byte for byte and retain legacy LF uninstall ownership (#66). - Give agnostic advice tests neutral project paths so checkout names cannot imply unselected providers (#67). - Reject runner timeouts above the Node timer limit before starting a lane (#68). - Store install and uninstall backups in the rules folder's gitignored `.backups/` directory and preserve user backups (#73). ### Changed - Refresh the metered provider model snapshot, checked 2026-10-16 against each provider's model list: the OpenAI second-opinion lane moves to `gpt-6.1-sol`, the Gemini long-context lane to `gemini-4-argon` and the xAI live-fast lane to `grok-4.3`; the snapshot now expires 2026-11-15. - Record optional subagent token usage and model identifiers, report supplied token totals, describe routing to cheaper models and disclose route-table prompt overhead (#69). ## [1.0.14] - 2026-10-10 ### Fixed - npm homepage now points at the product site. ## [1.0.13] - 2026-10-07 ### Added - Resolve current Claude tier models from official documentation with `aunx models --probe`, explicit candidate approvals for new families and price increases, optional isolated alias canaries, validated installer snapshots through `--models`, and offline checks against installed agent definitions (AUN-1345). ### Fixed - Reject incomplete family approvals before optional alias probes and report model snapshot changes during reconfiguration (AUN-1345). - Refresh the Anthropic gateway compatibility IDs from the official model overview checked on 2026-10-07 (AUN-1345). ## [1.0.12] - 2026-10-07 ### Fixed - Preserve backslashes before pipes in rendered role tables and catalog facts, including nested fact keys. - Use explicit registry comparisons and static Node fixtures with data arguments; execute the watchdog test harness from a file to make the existing boundaries clear to code scanning. ## [1.0.11] - 2026-10-06 ### Added - Add the "Skill packs on Agensi" section to README.md and beginner and intermediate pack links to llms.txt (closes #57). ## [1.0.10] - 2026-10-06 ### Fixed - Apply plain routing rules for every supported main agent, preserve manual copy snippets, replace older applied blocks and retain uninstall compatibility (#51, #52). - Refuse home-level configuration before dry-run output, with one message naming the refused root (#53). - Print runner usage successfully for `--help` and `-h`, including through `aunx cli-run` (#54). - Skip writes and backups for identical files, never back up machine-owned configuration, and remove the rules folder after a clean uninstall (#55). - Correct the release workflow comment and README proof-expiry wording (#57). - Restore the 0.1.14 changelog heading from its tagged release history (#58). - Keep each release's notes separate on the website and display angle-bracket placeholders and HTML examples literally (#58). - Keep the UTC `Z` in backup names and document the timezone (#58). - Read installed lane selection as data for `aunx cli-run --doctor`, with guidance for custom rules folders (#58). - Restore the original settings bytes on uninstall when remaining JSON matches the pre-install backup (#58). - Accept catalog IDs and numbers in interactive AI picks and retry once after an invalid answer (#58). - Keep existing task briefs with a friendly message and list commands for unknown `aunx` subcommands (#58). - Show route roles consistently, with agent names in a separate field (#58). - Pluralize level requirements and label vendor install-script links accurately (#58). - Describe bulk work as running through cli-run only for an assigned runner lane at level 2 or higher (#58). ### Security - Skip Git probes for the audit effort floor and disable repository-configured helpers for remaining effort probes. - Verify installed hook executables before registering them, upgrade unchanged owned hooks and refuse unverified activation. - Refuse double quotes in install paths before rendering systemd commands. - Replace generated files through same-directory temporary files and rename, so hard links cannot redirect writes. - Make failure lines terminal-safe for every provider lane. - Parse repeated route markers with a bounded linear scan that allows nested comments. - Refuse non-regular or oversized lanes configuration before reading it. - Log route lane names only from the known catalog, agent names, and project custom subagents, recording other values as invalid. - Open rules files without blocking and verify their identity after opening to prevent a replacement-file race. - Create watchdog timeout markers as directories so an unlinked marker cannot truncate a symlink target. - Pin the Level 3 images to `ghcr.io/berriai/litellm:v1.104.0` and `ollama/ollama:0.35.1`, the newest releases with the fewest advisories, and renew the dated advisory exceptions for what remains to 2026-11-05 (#56). ### Changed - Exclude the source-only skill-pack generator from npm packages and check for `zip` before packaging, with an installation hint (#57). - Print provider, model and effort as separate route fields while preserving the run log schema (#58). - Limit the contributor license agreement to work intentionally submitted through pull requests and name that agreement in the README (#58). ## [1.0.9] - 2026-10-04 ### Added - Generate standalone beginner and intermediate Agensi skill packs from shared templates, with setup guidance, the bundled intermediate runner, zip packaging and drift checks. ### Changed - LICENSE names Auny LLC as copyright holder; outside contributions now require the CLA in CLA.md. ## [1.0.8] - 2026-09-30 ### Changed - AGENTS.md is the single source of agent instructions. CLAUDE.md imports it so Claude Code loads the same rules as every other agent. - Merge the contributor rules that had diverged between AGENTS.md and CLAUDE.md. - The feature request form asks for an observable success check. ## [1.0.7] - 2026-09-30 ### Fixed - Prefer an already-connected Claude worker MCP service in host routing, with the native CLI retained as a standalone fallback. Route suggestions and generated instructions describe the session, result, permission and environment boundaries; no MCP installation or automatic transport retry is added. - Enable the native `claude` CLI worker with print-mode JSON completion checks, existing permission rules, model and effort overrides, failure classification and shared timeout/cancellation handling. Codex-primary stacks can assign independent review to Claude Code; Claude-primary stacks retain Codex review. Generated lanes use executable names while manifest AI identities retain installer IDs. Claude fixtures record native success and authentication failures. - Keep supported lanes usable when an edited or unverifiable older runner is kept during an upgrade. Withhold unsupported lanes and their defaults from `lanes.json`, and report how to enable them with `--upgrade-runtime`. - Classify Claude's expired OAuth session and `Failed to authenticate` errors as auth (exit 14). - Match Claude HTTP codes only in API status fields or `API Error:` prefixes, so a 400 error mentioning 429 tokens stays rejected (exit 16). ### Changed - Check and pin the Claude lane at 2.1.285, and replace its hand-written success fixture with a captured restricted live success at that version. ## [1.0.6] - 2026-09-29 ### Added - The hermes lane takes `--provider` and `"provider"` in `bin/lanes.json` defaults, so a pinned model goes to a provider that serves it. A provider needs a model with it (a flag, a default or `HERMES_INFERENCE_MODEL`); otherwise cli-run refuses before the lane starts. Runs log `provider_requested` and `provider_source`. - `--doctor` notes a hermes model pinned without a provider, and a provider pinned without a model. The terminal route line shows the provider. ### Fixed - A hermes model/provider mismatch (an upstream "model is not supported" or `model_not_found`, for example `grok-4.6` sent to `openai-codex`) is now class `rejected`, exit 16, with a problem line naming the mismatch and a fix pointing at `hermes model`. It was reported as a vague nonzero exit. - A pinned hermes route (`--model` or `--effort`, by flag or `lanes.json` default) no longer fails every run. cli-run placed those flags between `-z` and the prompt, and `-z` takes the prompt as its value, so Hermes refused the arguments ("argument -z/--oneshot: expected one argument"). Route flags now come before `-z`, and a Hermes argument error is reported as such rather than as a toolsets error. ### Changed - The product website generates public Markdown mirrors, llms discovery files and agent navigation guidance from its existing documentation allowlist on each Git build. ## [1.0.5] - 2026-09-28 ### Changed - The proof page keeps only figures that measure this package. The two browser token figures measured the author's own browser subagent, which model-orchestrator does not ship, and are removed from the proof data, the site and the trailer. - An expired proof figure is a signal to re-measure: it warns and never fails tests, CI or a release. The product site leaves expired figures off. ## [1.0.4] - 2026-09-28 ### Security - Level 3 images move to `ghcr.io/berriai/litellm:v1.100.3` (from v1.99.1, 23 advisories to 9) and `ollama/ollama:0.34.4` (from 0.33.3). The advisories still inside those vendor images are recorded as reviewed exceptions that expire on 2026-10-28, so the check flags anything new and asks for a fresh review then. ### Fixed - The container advisory check records a Go binary's own unversioned module by name instead of reporting the whole image as unknown; any other package without a version still makes the image unknown. ## [1.0.3] - 2026-09-28 ### Security - Weekly audit jobs use an explicit child environment allowlist, keep selected worker configuration overrides out of probes, and preserve stored sign-in paths. See the generated jobs README for migration (#45). - Catalog package and container pins get advisory checks with explicit coverage, dated machine-readable results and expiring advisory-specific exceptions. See [catalog advisory checks](docs/catalog-advisories.md) for operation and limitations (#46). ### Added - `aunx route-metrics --summary` compares the route your agent named in each reply with the delegation that actually followed, from data the log already records: the share of sessions that match, sessions that named a route and dispatched nothing, and sessions that dispatched without naming one. ### Fixed - The product website navigation follows the section being read, keeps the active sidebar link visible, and labels its portfolio breadcrumb Home. ## [1.0.2] - 2026-09-28 ### Fixed - A refused symlinked or changing file (such as `MANIFEST.json`) now names its path and the fix: replace the link with the file it points to. Since 1.0.1, rerunning the installer over a symlinked manifest stops instead of treating it as a fresh install. - A path shown in a refusal escapes control characters, so a crafted file name cannot write terminal escape sequences; a directory or other non-file gets "expected a regular file" without symlink advice. - The acceptance-check protocol states the timeout's reach: it stops the command's process group, and a descendant that starts its own session (for example with `setsid`) keeps running. ### Added - Product website at `model-orchestrator.aunysillyme.dev`, with repository-generated documentation, changelog, and the model-orchestrator trailer. Main-branch pushes rebuild the site through Vercel; the published version is checked against npm. ## [1.0.1] - 2026-09-27 ### Security - `aunx route-metrics` always runs the packaged metrics implementation. A checkout can no longer substitute its own JavaScript when a user requests a summary. - Installer and uninstall manifests use bounded regular-file reads with symlink refusal. Acceptance-check and routing JSON reads share the same bounded reader. - Acceptance-check timeouts and interrupts clean up the command's process group or Windows process tree, including ordinary descendants. - Weekly gateway probes pass credentials through stdin instead of temporary files, reject multiline keys, and remove gateway/provider credentials from audit subprocesses. The service now reads a separate `weekly-audit.env`; see the generated jobs README when upgrading. - Newly generated companion launch commands use the catalog's exact versions, and npm publication uses a reviewed exact npm version. Existing client entries are preserved; follow the [companion upgrade steps](docs/install.md#upgrading-companion-launch-commands). ### Fixed - Security and installation documentation now match current activation rules, model inheritance, companion setup and credential boundaries. ## [1.0.0] - 2026-09-27 ### Added - **The `aunx` command.** The existing package now ships a short command alongside `model-orchestrator`. Run the installer with `aunx`, call an AI through `aunx cli-run`, scaffold a task brief and context file, execute acceptance checks, get a routing suggestion or summarize your local routing activity. - **A build process from requirements to verified use.** Shared context, executable acceptance checks, live capability probes, scoped assignments, background heartbeat guidance, split-build ownership and one independent audit step with a companion reviewer. - **Reproducible proof.** Measurement scripts, dated results with method and sample size, a generated proof page and a weekly refresh workflow. The test suite rejects expired figures. ### Changed - **Automatic project activation:** interactive installs apply the main agent's catalog-supported rules block and Claude Code hooks under the existing single confirmation, with backups. The edit screen and `--no-apply` keep activation manual; headless `--yes` requires `--apply-snippets` as before. - **Remaining actions:** every non-dry install runs the local CLI presence check automatically. "What's left for you" omits completed activation and informational items, checks Codex's reliable sign-in status, and makes other sign-in instructions conditional. Live canaries remain opt-in. - **Companion registration and cleanup:** selected companions register in supported project MCP config when activation is enabled; global configuration stays manual. Activation ownership lets uninstall remove unchanged applied blocks and added hooks or MCP entries while preserving unrelated content and backups. - **Stack-dependent assignment:** planning, building, review, verification, research, bulk work, reading and private work are assigned from selected capability facts. Generated stack tables explain each choice; the manifest stores the current assignment and `aunx route` reads it. Independent review requires a known different model family, and private work requires local execution. - **Model tiers:** generated agent definitions use planning, working and cheap model tiers. Current plan-to-model mappings are unverified, so definitions leave the model to the user's tool configuration while preserving effort guidance. - **One-confirm installation:** interactive setup detects available AI tools and shows a proposed level, main agent, paths and role table before one confirmation. The edit menu changes individual settings; an empty detection asks for the user's AIs first. Headless `--yes` still requires explicit level and AI selection. - **Capability catalog:** AI entries carry `summary` and `facts`; role claims and duplicated capability fields are retired. The generated catalog marks unknown facts and inherited vendor claims as unverified. - **Companions are opt-in.** Default and `--yes` installs select none. Missing tools appear together under "Install these yourself", with their official commands and links. - **Public terms match the work.** Task brief, context file, acceptance checks, subscription lanes, pay-per-token lanes, planning model, working model and cheap model. - **Documentation starts with the model router.** New command examples, search-phrased questions, a trailer hero, an install walkthrough and a concise security-review history with regression evidence. - **A smaller publication surface.** Development-only `scripts/` and the old raw audit brief are excluded; reproducible proof scripts remain available. ### Fixed - **Coherent level installs:** routing uses installed agents or available role fallbacks, review guidance accounts for the main agent's model family, smoke paths are quoted, and README first reads and output-contract checks match the level and selected workers. - **Level 3 setup:** initialize the configured local model inside Compose, verify `local-small`, and configure the scheduled service's executable search path. - **Accurate level descriptions:** qualify activation and agent sets by main agent, keep companions opt-in, and describe the weekly job's fixed lane, observed data and configurable budget and privacy policies. - **Post-install review round:** oversized settings and MCP JSON files are refused before any write, the rules block keeps a CRLF file's line endings, Claude Code sign-in is hidden only when its status reports signed in, Ollama install text appears only when it is absent and names the configured model, CLI main agents without a rules file get accurate wording, and a new rules file is labelled "create". ### Removed - **Automatic vendor installation.** The installer never runs third-party installs. `--no-install` remains accepted for existing scripts. ### Upgrading from 0.1.x - Keep your existing `--dir` and `--project`. Run with `--update-docs --dry` first, then remove `--dry` to apply. - Unedited `TASK_BUNDLE.md` migrates to `TASK_BRIEF.md`; an edited legacy brief is kept and named for manual migration. - Unchanged runtime files upgrade automatically. `--upgrade-runtime` replaces runtime files only and preserves document edits. - Existing codecalc guides and snippets stay managed; uninstall removes them only when unedited. New installs select companions explicitly with `--tools`. - Every previous installer flag remains accepted. Runner exit codes, logs, hook contracts and safe uninstall behavior remain compatible. ## [0.1.35] - 2026-09-25 ### Changed - **The description names all three things the installer writes.** npm, the GitHub About box and `package.json` now read: rules, subagents and a lane runner tell agents which model to use. The README opening already listed the lane runner; the description stopped at rules and subagents. - **Every GitHub topic is an npm keyword.** `coding-agents` was a topic with no matching keyword. ## [0.1.34] - 2026-09-24 ### Changed - **The package description says what it installs and who picks the model.** npm and the GitHub About box now read: rules and subagents tell your agent which model does each task, matching the README opening; the old line read as if the package itself sent work to a tier. ## [0.1.33] - 2026-09-24 ### Added - **Opt-in snippet application.** `--apply-snippets` applies a replaceable Claude Code rules block and merges hooks with timestamped backups, validation before writes, dry-run previews and manual uninstall guidance. ([#41](https://github.com/aunysillyme/model-orchestrator/issues/41)) - **Model orchestrator or a model proxy.** README and `llms.txt` explain the request layer, when to pick each approach and how they compose. ([#44](https://github.com/aunysillyme/model-orchestrator/issues/44)) ### Fixed - **Selected lane guidance.** Delegation rows and related cost and research advice render from selected catalog lane categories, keeping absent categories out of active picks. ([#42](https://github.com/aunysillyme/model-orchestrator/issues/42)) - **Rules folder relocation.** Project-contained snippets use relative paths; external rules paths carry relocation guidance. Installer hooks that read rules honour `MODEL_ORCHESTRATOR_RULES_DIR`, and the generated README names the path case. ([#43](https://github.com/aunysillyme/model-orchestrator/issues/43)) ## [0.1.32] - 2026-09-23 ### Changed - **README shows what the install gives you, not a file dump.** The 50-line dry-run file list under the demo became a six-row table naming each part and what it does for you; the preview command stays, and the full file list is one link away in `docs/install.md`. ## [0.1.31] - 2026-09-23 ### Added - **Manifest-based uninstall.** `--uninstall --dir --project ` removes managed files whose content matches the recorded hash, keeps and names edited files, and preserves files outside the manifest. `--dry` and `--dry-run` preview the same removals. The manifest is the last file removed and stays when edits remain. New installs record the directories they create so uninstall can remove them when empty; older manifests leave directories in place. The command prints the manual steps for removing pasted rules and merged hooks. A missing manifest exits 2 and names its expected path. - **Activation and related tools in the README.** A captured install supplies the rules, hooks and smoke-test steps. A shared table links agent-personalizer and website-build-skill, and a short uninstall section links the full instructions. ### Changed - **Presentation leads with the task and payoff.** The opening states that the rules tell your agent which model handles each task, followed by the problem, the setup and the routing log. README sections follow the install flow, with platform and vendor details kept in collapsed sections. Contributing names new catalog entries, vendor fixtures and documentation fixes as welcome contributions. - **Short package description and consistent names.** The description fits the search-card budget while preserving the purpose clause. The opening and agent summary name Antigravity (Google). `llms.txt` keeps reference links together and gives plans and automatic effort their own section. - **Positive section headings.** The plugin README names what the hooks do and what npx installs; codecalc explains where it fits; the pull request template names work kept for a later change. Safety guarantees retain their explicit wording. ### Fixed - **Setup output matches the generated files.** The plan counts subagents and hooks separately from its file list, and the README contains captured dry-run output with project-relative paths. `--list` prints each AI's catalog install instructions and sign-in note. The README describes how reruns preserve documents, rewrite machine-owned configuration and upgrade untouched runtime files, and its routing link points to the detailed document. ### Security - **Uninstall validates the entire manifest before removing files.** Absolute paths, traversal, symlinked entries, malformed hashes and mismatched target roots are refused. Foreign files in shared project folders stay. A rerun targeting another project carries previous ownership records only for roots that still match. Regression tests cover these refusals, edit preservation, directory ownership, changed projects, legacy manifests, previews and missing manifests. ## [0.1.30] - 2026-09-22 ### Fixed - **Every hook count now matches the files on disk ([#35](https://github.com/aunysillyme/model-orchestrator/issues/35)).** The generated `CLAUDE.snippet.md` said "Two hooks were written" and named only `route-gate.mjs` and `subagent-context.mjs`, while a claude-code install writes and wires `route-metrics.mjs` too. It now names all three and how to read the metrics log. The same pass corrects `llms.txt` and `docs/install.md` (three hooks on a full install), `templates/agents/README.md` (lists `route-metrics.mjs`), and the README plugin section, which said the plugin ships three hooks: it ships the two read-only ones, and `route-metrics` comes only with the npm install. A new test fails if the snippet names fewer hooks than the plan writes. ## [0.1.29] - 2026-09-21 ### Changed - **The seven principles state what to do, rather than what fails.** "A gate you cannot fail is not a gate" became "Every gate can come back wrong. A checkpoint earns its place by being answerable both ways"; "Exit 0 is not a deliverable" became "Check for the artifact. A deliverable is a file, a commit or a line you can point at"; "A write nobody can find again did not happen" became "A write stays findable". Same rules, same gates, stated forward. This finishes the copy pass 0.1.28 started, which left the principle list untouched. ## [0.1.28] - 2026-09-21 ### Added - **A recording of the command doing its job, at the top of the README.** `docs/demo.gif` (60 KB) shows `npx model-orchestrator ... --dry` typed and run: the level, the AIs, both target folders, all 38 files it would write, and the closing line that nothing was written. `scripts/record-demo.sh` re-records it from the PUBLISHED package inside a temp folder, so the frames stay the program's own output rather than a staged screen, and anyone can reproduce them with `brew install asciinema agg`. The text plan stays in the README under the image, and the image carries alt text describing what it prints, so the page still reads with images off. - **`--summary` now answers the question the package exists for: how much work left the main session.** `work sent off the main session` counts covered turns whose route marker named any lane that is not an inline name, over covered turns. It is derived only from lane names the log already holds, with no price table and no token estimate, because the log holds neither. `--inline a,b` renames what counts as inline, since the lane vocabulary belongs to your own `ROUTING.md`. A log with no lane markers yet says so instead of printing a number. ### Changed - **Every line of user-facing copy states what the package is and does.** The opening bullet was "What it is not: a proxy, a gateway or an API router", which told a new reader what to stop expecting before they knew what they were looking at. It now reads "Where it sits: above the request layer. Your agent reads the rules and picks the lane", and request-level routers are described as composing underneath rather than as the thing this is not. Same for the plugin section, the companion-tool intro and the gateway question in Common Questions. `test/copy.test.js` keeps the boundary it was written to protect: the OVERCLAIM guard is unchanged, and two positive phrases are now required in both README and `llms.txt`, so the claim cannot quietly widen and the copy cannot quietly lose it. ## [0.1.27] - 2026-09-21 ### Changed - **README restructured for a first-time reader: 32,083 bytes to 17,586, with the first screen now a claim, the install command and a real `--dry` plan.** The old opening was a 200-word paragraph followed by "What it is not", the flag-conflict rules and the uninstall procedure, all before the reader had seen the tool do anything. The detail moved rather than went away: `docs/install.md` (every flag, the two target folders, headless examples, the full file list), `docs/how-it-routes.md` (role, complexity and stakes; the three verifier agents; pinning model and effort), `docs/guarantees.md` (enforced by code, delegated to a vendor flag, or only an instruction), `docs/companions.md` (the full companion-tool table). `docs/README.md` and `llms.txt` index all four. Platform support and the vendor compatibility table stay in the README inside collapsed `
` blocks, because `scripts/gen-catalog.js` writes the vendor table between markers there and `test/prose.test.js` requires the skipped-test explanations to live in the README; collapsing them keeps both mechanisms pointed at the same file. The opening line still satisfies `test/copy.test.js`: it names the package, carries the shared purpose clause, and keeps the 0.1.11 correction that it does not select models itself. ## [0.1.26] - 2026-09-19 ### Added - **obsidian-tc on Windows: the answer is that no client needs a `cmd /c` snippet, and `OBSIDIAN-TC.md` now says so with the code it was read from.** All five obsidian-tc snippets spawn `"command": "npx"`, and on Windows `npx` is `npx.cmd`, which a bare `CreateProcess` will not start. Each client was read instead of assumed, and all five resolve it themselves: Zed hands the command to the system shell (`crates/context_server/src/transport/stdio_transport.rs`, `ShellBuilder::new(&Shell::System, ..)`, from its PR #42382, 2025-12-10), VS Code's `formatSubprocessArguments` in `src/vs/workbench/api/node/extHostMcpNode.ts` resolves the executable and re-spawns with `shell: true` for a `.bat` or `.cmd`, the Codex CLI calls `which::which_in` in `codex-rs/rmcp-client/src/program_resolver.rs`, and Cursor and Antigravity's `agy` both reach `npx` through `@modelcontextprotocol/sdk`, whose `client/stdio.js` imports `cross-spawn` and re-invokes a non-`.exe` as `%COMSPEC% /d /s /c`. That last point is wider than those two clients: every published SDK from 1.23.0 through 1.30.0 depends on `cross-spawn ^7.0.5`, so `shell: false` in that transport is not the whole story. The doc also names the one Windows case that can still fail and is not about `.cmd`: Zed prefers PowerShell, which resolves a bare `npx` to npm's `npx.ps1` shim, and a `.ps1` will not run under the `Restricted` execution policy that is Windows' client default. A test pins the snippet set, fails if any `.windows` file appears, and fails if the doc drops a citation. Not run on a Windows machine. ### Fixed - **Context7's Windows guidance said something untrue about Zed, and its `cmd` wrapper was missing `/d`** ([#34](https://github.com/aunysillyme/model-orchestrator/issues/34) follow-up). 0.1.25 shipped `mcp/context7.zed.windows.settings.json` on the premise that Zed spawns `"command": "npx"` directly and so cannot start it. Reading Zed's own source for the obsidian-tc work above showed it has launched MCP stdio servers through the system shell since PR #42382 (2025-12-10), so a current Zed starts the plain snippet. The file stays, because it is still the fix for an older Zed and for the PowerShell execution-policy case, but `CONTEXT7.md` no longer sends every Windows user to it and now carries both reasons. The snippet's args change from `["/c", "npx", ...]` to `["/d", "/c", "npx", ...]`: without `/d`, `cmd` first runs whatever sits in the Command Processor `AutoRun` registry value, which can print non-JSON into the protocol stream. `cross-spawn` passes `/d /s /c` for the same reason. The `#34` test follows the new args and now also fails if the doc stops naming why a current Zed does not need the file. ## [0.1.25] - 2026-09-19 ### Fixed - **Context7 on Windows: a Zed snippet that can start the local server** ([#34](https://github.com/aunysillyme/model-orchestrator/issues/34)). `mcp/context7.zed.settings.json` spawns `"command": "npx"`, and on Windows `npx` is `npx.cmd`, a batch file a client cannot spawn as a bare command. New `mcp/context7.zed.windows.settings.json` runs `"command": "cmd"` with `"args": ["/c", "npx", "-y", "@upstash/context7-mcp@"]`, the shape Context7's own client guide ships for Windows. `CONTEXT7.md` gains a "Local `npx` on Windows" table saying which Zed snippet to use on which OS, and the same by-hand change for any other client taking the local alternative. The remote snippets spawn nothing and are unchanged. A test pins the Windows args to the POSIX args behind `/c npx` and the catalog pin; not yet run on a Windows machine. ## [0.1.24] - 2026-09-18 ### Added - **Context7 as a third companion tool, paired with codecalc.** [Context7](https://github.com/upstash/context7) (Upstash) hands the agent current, version-specific documentation and code examples for any library, SDK, API or CLI, hosted or run locally with `npx`. It answers what a library is documented to do; codecalc still answers what the code actually does by running it. A new protocol, `protocols/docs-then-prove.md`, states the rule the pairing serves: pull current docs before writing against anything unconfirmed this session, then a run proves it, and where a doc and a run disagree the run wins. Optional and off by default, like obsidian-tc (`--tools context7`, or the interactive question); unlike the other two companions it always makes a network call, so it is the one to skip offline. `src/catalog.js` carries the entry (repo, role, install, requirements, the clients it self-registers with); `CONTEXT7_STATUS` renders both selected and not-selected wording the same way `CODECALC_STATUS` and `OBSIDIAN_TC_STATUS` already do, at every level and in `ROUTING.md`. - **`templates/tools/context7/CONTEXT7.md` plus seven per-client `mcp/` snippets**, each read from that client's own docs rather than shared across clients that do not actually share a config shape: `context7.claude-code.mcp.json` (`"type": "http"` next to `url`, required or Claude Code skips the server), `context7.mcpServers.json` (Cursor's own documented shape, `url` with no `type`), `context7.vscode.mcp.json`, `context7.qwen.settings.json` (`httpUrl`, not `url`, plus the non-credential `Accept` header upstream ships), `context7.zed.settings.json` (local `npx`, pinned to the catalog's `context7` version), `context7.codex.config.toml`, `context7.agy.mcp_config.json`. Claude Desktop has no file: its remote connection is a UI step (`Settings > Connectors > Add Custom Connector`), documented in `CONTEXT7.md` instead of a snippet nothing there reads. - **Every context7 snippet ships keyless by default.** The anonymous tier works with no header, while an unexpanded or empty `Bearer ${CONTEXT7_API_KEY}` makes every call return "Invalid API key" (probed live against `https://mcp.context7.com/mcp`), and clients like Codex `http_headers` never expand it. `CONTEXT7.md` has a "Higher rate limits (optional key)" section with one mechanism per client: Codex `bearer_token_env_var`, Claude Code `${VAR}` expansion in `.mcp.json` headers, Claude Desktop's own Connectors key field, an exported shell variable for local `npx`, and "check your client's docs" where expansion is not confirmed; the fallback for a client that does not pass its environment to a spawned child is to stay anonymous, never to paste the key into a snippet's args. A test fails if a shipped snippet carries `Authorization` or `CONTEXT7_API_KEY`. ## [0.1.23] - 2026-09-15 ### Added - **`cli-run` says WHY a lane failed.** Every run lands in one class with its own exit code: `auth` 14, `quota` 15, `rejected` 16, `refused` 17, `cut_short` 18, next to the existing `empty` 10, `no_output` 11, `timeout` 12 and `unavailable` 13. A missing API key, a spent quota and an unknown model id used to share exit 10 or the vendor's own code, and each needs a different response: a missing key is not a model fault, and retrying a spent quota cannot help. Signals are read from each lane's authoritative error fields only, never the model's prose, with precedence auth, quota, rejected, refused, cut_short, empty. - **`refused=N` on every run.** Tool calls a hook or deny rule blocked, counted from qwen's `permission_denials`, agy's deny-rule steps, codex's router `Rejected(` lines, and grok's session transcript (its `sessionId` is charset-checked, the path is contained to the sessions root, lines must name the same session, and the read is capped at 5 MiB). `null` when a lane gives no signal. A deliverable with refused calls is still exit 0. - **A problem line and a fix line** on the terminal for every failure, and for an `ok` run with refused calls, so a calling agent can relay "this is what went wrong, this is the fix" and ask. - **Terminal output is redacted** before it prints: JSON credential keys, `Authorization:` values, bearer values, URL query credentials, and common key prefixes, redacted before any clipping. - **The durable log gains `class` and `refused`.** Both are fixed values; the log still never holds provider text, the problem line or stderr. ### Changed - **A nonzero vendor exit is no longer passed through as cli-run's exit code.** The class owns the code, and the vendor's own code stays in the log as `cli_rc`. A nonzero exit nothing else explains is `cut_short` (18), and it is still never `ok`. If a script compared `cli-run`'s exit code with a specific vendor code, compare `cli_rc` in the log instead; `!= 0` checks are unaffected. - **A lane killed by a signal, or output past the 16 MiB buffer, is `cut_short` (18)**, not 10. Exit 10 now means only an empty run or an unmet `--expect-*` contract. - **agy's judge refuses a non-object terminal `result` as `bad_last_event`** (was `bad_status`), and **qwen's judge refuses a non-string `error.message` as `error_message_not_string`**, so both classify as `cut_short`. hermes' stderr cause (degraded free tier, bad `--toolsets`) is now named in its detail line. ## [0.1.22] - 2026-09-12 ### Added - **SuperGrok Plus as a `grok` plan (high headroom).** xAI's pricing page lists it with "Significantly higher usage across Chat, Imagine, Voice & Build", so `--plans grok=supergrok-plus` now works and counts as a high-headroom lane for plan guidance and `--effort-auto`. SuperGrok Heavy stays out: the page states no Build usage for it. A test holds both. ## [0.1.21] - 2026-09-12 ### Added - **Subscription plans, stated by you and never guessed.** The installer asks which plan you hold for Claude Code, Codex, `agy` and `grok` (interactive, or `--plans codex=pro-20x,agy=ultra-5x`; `--plans none` clears). Each plan row in `src/catalog.js` carries only a name, a headroom level (`base`, `high`, `max`), its official source page and the date it was checked; no prices and no model ids, because both change faster than releases. `--list` prints them. - **Plan guidance in the generated docs.** The lanes table gains a `Plan` column, and `ROUTING.md` and `DELEGATION_MATRIX.md` gain a plan guidance block: high and max headroom lanes take volume (scoped builds, pre-ship second-family audits, first-pass research), base headroom lanes keep short second opinions. Headroom moves volume only; who reviews what does not change. - **`--effort auto` in `cli-run`.** Accepted as a flag or as a `lanes.json` default on every lane with an effort flag (qwen still refuses it). A call sizes from prompt length: under 4,000 characters is `medium`, otherwise `high`. A codex `--audit` always runs at `high` and logs the larger of prompt length and changed lines as evidence. Auto never resolves above `high`: `xhigh`, `max` and `ultra` are sent only when named. The durable log gains `effort_resolved`, `effort_basis` (`explicit`, `prompt_chars`, `audit_floor`, `none`), `effort_scope` and `effort_truncated`. - **Bounded change counting for audits.** Untracked files are listed NUL-delimited, only regular files are opened (FIFOs, devices and symlinks are skipped by `lstat`), and the pass stops at 200 files, 256 KiB per file, 2 MiB total or 2 seconds, marking the evidence truncated. An unborn `HEAD` or any git failure falls back to prompt length. ### Changed - **Automatic effort is opt-in.** A high or max headroom plan does not change `bin/lanes.json` by itself; `--effort-auto` (or yes to the question) writes `"effort": "auto"` for exactly those cli-run lanes. Claude Code is never written there, since it is not a cli-run lane and an entry for it would fail the whole file closed. With no plan stated, `bin/lanes.json` is byte-identical to 0.1.20 and `MANIFEST.json` records no plan keys. A re-run that omits `--plans` keeps the previous plans. - **Five documented Windows skips, not four.** The FIFO half of the new untracked-file test needs `mkfifo`; its symlink half runs everywhere. ## [0.1.20] - 2026-09-12 ### Added - **A Claude Code plugin.** `/plugin marketplace add aunysillyme/model-orchestrator`, then `/plugin install model-orchestrator@model-orchestrator`, installs `route-gate.mjs`, `subagent-context.mjs` and the eight subagents without merging a settings snippet by hand. The bundle lives in `plugin/`, listed by `.claude-plugin/marketplace.json` at the repo root. It passes `claude plugin validate --strict`, the check Anthropic's community marketplace review runs, and all eight checks of Sigistry's public plugin verification methodology (1.2), run standalone before release as a quality bar; the plugin is not listed there. - **The plugin is generated, never a second copy.** `npm run gen:plugin` renders `plugin/` from the same `templates/` the installer uses, and `test/plugin.test.js` fails when the committed bundle drifts from that, when `plugin.json`'s version is not `package.json`'s, when `hooks/hooks.json` references a hook that is not shipped, when a plugin hook gains a network call, a file write, credential access, dynamic evaluation or a subprocess, when an agent has no `tools:` line or a review-type agent carries Write or Edit, and when the plugin README loses its install commands. Each check was proved red against the real files before it was trusted. - **The plugin's route gate works without an install step.** A plugin cannot be rendered per project, so its `route-gate.mjs` reads the installer's default locations, `ai-orchestrator/ROUTING.md` then `ai-orchestrator/ORCHESTRATOR.md`, and takes the first that exists. Something at the first path that is not a readable file (a directory, a FIFO) is reported, never skipped for the second. With neither present it tells Claude on every prompt, and the user once at session start, to run `npx model-orchestrator`, so a project with no rules is never a silent no-op. ### Changed - **`builder`, `deep-planner` and `live-researcher` now declare their tools, for npm installs too.** Until now they carried no `tools:` line and inherited every tool the session had, MCP tools included. `builder` gets `Read, Write, Edit, Glob, Grep, Bash`; `deep-planner` gets `Read, Glob, Grep` (its prompt already says it never edits); `live-researcher` gets `WebSearch, WebFetch`. This narrows what those three agents can do in an existing install once regenerated: if you relied on `builder` calling an MCP tool, or `deep-planner` running a command, add the tool to that agent's `tools:` line or delete the line. - **`route-gate.mjs` takes a list of rules paths instead of one.** An installer render is a one-element list with no setup hint, so an npm install behaves exactly as before; a test pins that render. ### Fixed - **`route-gate.mjs` could emit more than Claude Code's 10,000-character hook output cap, on npm installs too.** The routing table was capped at 4,000 characters, but a fallback message embeds the resolved project path and the error text, so a 12,000-character `CLAUDE_PROJECT_DIR` produced 12,146 characters from an installer render. Every string the hook emits is now capped at 8,000 characters, with a test on both renders. Found by the pre-release audit round and reproduced before the fix. - **The plugin's hook-safety test could not see an async write or subprocess.** `writeFileSync?` matches `writeFileSyn` and `writeFileSync`, never `writeFile`, and every `process.env` read was exempt. The check now matches the Sync and async form of every file write and subprocess call, refuses dynamic `import(` and `require(`, allows static imports of `node:fs` and `node:path` only, requires `openSync` to open read-only, and allows no environment variable but `CLAUDE_PROJECT_DIR`, with a red case for each. Found by the same audit round. ### Not changed - `route-metrics.mjs` still installs with `npx model-orchestrator`, unchanged. It is left out of the plugin only, because it writes a log to disk and the plugin ships only hooks that read. ## [0.1.19] - 2026-09-12 ### Added - **The `route-gate.mjs` non-regular-file guard is now tested on every OS, not only where `mkfifo` exists.** The FIFO test is the only one that can prove the HANG the guard exists to prevent (a naive `readFileSync` on a writer-less FIFO blocks forever), and Windows has no `mkfifo` to build one, so that test was skipped there and nothing exercised `!st.isFile()` on Windows at all. A directory at the same rules path reaches the same guard before any `open` or `read` call, on every OS, so the guard itself is covered everywhere and the win32 skip is no longer its only coverage. - **A guard that refuses an undocumented test skip.** `test/prose.test.js` pins each skip to its file, its exact marker and a phrase the README has to carry, then counts every `{ skip:` in the suite and fails if the totals disagree. Proved in both directions: adding a skip anywhere fails it, and removing a skip's explanation from the README fails it. ### Fixed - **The README's Windows skip list named three of the four skips.** The `mkfifo` skip in `test/hooks.test.js` had never been documented, in the README or the changelog, while the sentence above it read "a few narrow skips remain" and enumerated the rest. A skipped test reads as a test that passed, so an undocumented skip is a coverage claim nobody made deliberately. All four are named now, the conditional `statSync().mode` assertion is labelled as the one-assertion case it is rather than a skipped test, and the sentence says outright that the list is enforced by a test rather than maintained by hand. ## [0.1.18] - 2026-09-11 ### Fixed - **The level 3 box's weekly audit could record a hung `--version` probe as a version string instead of "UNVERIFIED: timed out".** `weekly-audit.sh`'s `killtree` killed a hung process's children before the process itself, so in that gap the probe's own shell could print and exit 0, and `bounded()` reported success. Seen once on macOS CI ("codex never" in the report); 0 of 20 local runs reproduced it, so it is rare but real on the box. `killtree` now freezes each process (SIGSTOP) before walking its children, and the watchdog leaves a marker when it fires so `bounded()` returns 124 whatever order the kills land in. A new test forces the bad ordering and checks the timeout still reads as one. - **On Windows, `bin/cli-run.mjs` could not run any lane at all: `spawn()` threw `EINVAL` for every `.cmd` binary, which is how npm installs every agent CLI there.** Since Node's fix for CVE-2024-27980 (18.20.2, 20.12.2, and every 22.x), spawning a `.bat`/`.cmd` target without `shell: true` throws instead of silently running it through an unsafely-escaped `cmd.exe`. This was a real, shipped defect, not a test gap: a Windows user following this README could not have run a single lane before this release. `bin/cli-run.mjs` now resolves the `.cmd` shim to the Node script npm's own `cmd-shim` tool wrote underneath it and spawns Node directly on that script (`resolveCmdShim`, `windowsSpawnPlan`), so a prompt (untrusted text this tool does not control) never passes through a shell at all in the common case. A lane whose `.cmd`/`.bat` cannot be resolved that way (an old or hand-edited shim) is refused with exit 13 and a message saying how to fix it, never run through `cmd.exe`: a batch file re-reads its arguments through `%*` after `cmd.exe` has parsed them once, and no escaping fully contains a prompt through both passes. The escaped `cmd.exe` path (the algorithm documented at [qntm.org/cmd](https://qntm.org/cmd) and used by `cross-spawn`, with `windowsVerbatimArguments`) survives only as an explicit opt-in for the installer's own `npm install -g `, whose arguments never include user text. Verified against the real, byte-for-byte output of `cmd-shim@9.0.2` (the package npm itself uses), not a guessed shape; the escaping is pinned to exact expected strings for `&`, `|`, `^`, `%`, `"`, a trailing backslash and a literal newline in `test/judges.test.js`. - **A reconfigure's terminal report (`runtime upgraded:`, `runtime CONFLICT, kept:`, `documents kept:`, and the rest) printed a Windows install's file paths with backslashes**, while every other path this tool prints in generated text uses forward slashes; `src/install.js`'s manifest key was already posix-normalized, but the label built alongside it for the human-readable report was not. `writeFiles()` now builds both from the same posix-normalized value. - **A `--dir` outside the project root, or the `vm/` level-3 templates' `INSTALL_DIR`/`INSTALL_DIR_SH`/`INSTALL_DIR_SYSTEMD`, ran through this host's own `path.resolve()`, which reads a leading `/` as drive-relative on win32**, wrong for both: the `vm/` templates describe a REMOTE Linux box (`weekly-audit.sh` is bash, `weekly-audit.service` is a systemd unit, neither of which can run anywhere but Linux), and the outside-project case is documentation prose, not a local filesystem path. An absolute `--dir` given as a bare POSIX path now renders unchanged on every host for both; a real local Windows path (one naming a drive) is untouched, since that case never took this branch. - **`killTree`'s win32 branch spawned a bare `taskkill`, which depends on PATH containing `System32`; when it does not, the spawn's `ENOENT` reaches this tool as an unheard `error` event on the returned process and crashes the whole run over what should be a best-effort cleanup step.** Found on `windows-latest` CI the first time a lane actually ran end to end there, once the `EINVAL` fix above stopped hiding it. `taskkillPath()` now resolves the executable under `%SystemRoot%` (falling back through `%windir%` to a fixed path), independent of PATH, and `killTree` attaches an error listener so a spawn failure can never crash the wrapper. - **`bin/cli.js`'s own opt-in `npm install -g ` prompt had the identical `EINVAL`-shaped defect as the lane spawn above, in a different file**, since it called `spawnSync('npm', ...)` directly with no shell. It now resolves `npm` with `which()` and runs it through the same `windowsSpawnPlan()` `bin/cli-run.mjs` exports, instead of a second, duplicated fix. ### Changed - **The three Windows test-skip groups tracked in [#30](https://github.com/aunysillyme/model-orchestrator/issues/30) are gone, replaced by two narrower, individually-justified skips found by actually running the unskipped suite on `windows-latest` CI, plus the one pre-existing skip this pass never touched (`statSync().mode`'s executable bit; NTFS has nothing equivalent).** `test/cli.test.js`'s fake lane binaries now install as an npm-style `.cmd` shim (verified against the real `cmd-shim@9.0.2` output) pointing at a small Node script, the same shape a real vendor CLI's install takes through `windowsSpawnPlan()` above, instead of a bespoke `sh.exe` bridge that never exercised cli-run.mjs's own spawn path at all; every test in that group but one, and the `#12` upgrade-path pair (fixed by the report-label change above), now runs unconditionally. `test/install.test.js`'s 7 formerly-skipped tests split on what their path assertion actually describes: the ones naming a `vm/` remote-box path stay literal POSIX (now true on every host, per the `INSTALL_DIR*` fix above); the ones naming a LOCAL path (where this run wrote files on this host) now build their expectation with `resolve()`/`join()` instead of a hardcoded POSIX literal, so they assert the same real, platform-native value the product renders rather than a string that only happened to match on POSIX. Two of those seven also had their own, separate bug once actually run on Windows: their fake `curl`/`jq`/`node`/`codex` binaries were placed on a hand-built PATH containing the literal strings `/usr/bin` and `/bin`, which name nothing on that OS; they now prepend the stub directory to the REAL `process.env.PATH` (this job already runs under Git Bash, so that PATH already carries what `bash` itself needs) instead of replacing it with a POSIX-only guess. - **New skip: a lane dying mid-run from a real POSIX signal genuinely cannot be reproduced on win32.** cli-run.mjs's `r.signal || r.status === null` branch exists for a real lane crashing or being sent a signal, but a real Windows lane is a plain `node