CareerRat
Advanced

Why Only Two Supported Engines?

Upstream projects list ten CLIs. CareerRat lists two. Here's the bar the other nine don't clear.

CareerRat's own runtime registry detects eleven AI CLIs: Claude Code, Codex, Gemini CLI, OpenCode, GitHub Copilot CLI, Qwen Code, Antigravity, Hermes Agent, Amp, Goose, and Droid. Only two, Claude Code and Codex, are supported. That gap looks like an oversight until you look at what "supported" means, and what else CareerRat checks before it will actually use a CLI.

Other agent-CLI compatibility lists usually mean "can read a markdown file and run bare." CareerRat's supported flag means something stricter, but it is a static design decision, not a live check. It marks a CLI that CareerRat has built an accepted adapter and a non-interactive permission boundary for. Claude Code and Codex stay supported even if the CLI is not installed, the user is not signed in, the installed version is below CareerRat's version floor, or the completion probe fails. Four checks decide whether a supported CLI is actually usable right now.

The four gates

The supported flag. Each entry in the runtime registry (src/core/ai/installed-runtimes.mjs) carries a supported: true or false. Only claude and codex are true. Selecting an unsupported runtime is rejected outright, both in the settings route (src/cli/installed-runtime-route.mjs) and in the AI call router (src/core/ai/call-ai.mjs).

The per-protocol boundary. A supported runtime doesn't just get invoked, it runs inside a locked-down sandbox CareerRat builds itself. Claude Code runs with defaultMode: "dontAsk", Bash, Edit, Write, and Agent denied outright, and a blanket denyRead rule (//**) paired with an allowRead list holding only the paths the skill needs. Codex runs with --sandbox read-only, --ignore-user-config, --ignore-rules, and a long list of disabled features (shell tool, browser use, computer use, multi-agent, plugins, and more). Neither CLI's default mode looks like this; CareerRat constructs it per call.

The completion receipt. Before CareerRat trusts a runtime for real work, it runs a smoke test: ask the CLI, inside that same sandbox, to return one exact JSON object matching a fixed schema (COMPLETION_SMOKE_SCHEMA) with a single receipt field and nothing else. The probe requests no tools and reads no candidate or workspace files. Both probes run inside a scratch working directory CareerRat creates for the probe itself. Codex also gets a temporary schema file; Claude Code receives the same schema inline through --json-schema. Neither CLI sees anything that belongs to the candidate. An exact match proves the CLI can complete a bounded, structured request under the configured sandbox. It doesn't independently exercise every deny rule; a CLI that accepted the sandbox flags but quietly ignored them could still pass.

The binary fingerprint. Before CareerRat runs real work through a runtime, it re-checks a SHA-256 hash of the executable against the one it resolved at detection time. If the binary on disk changed since detection, the identity check fails and the workflow execution is refused. Settings, authentication, and ACP probes don't independently fingerprint the executable. Tool-bearing Claude Code runs are the exception: they check identity before the boundary-version probe, then check it again immediately before the workflow process spawns.

A CLI clears "supported" by being listed as one in the registry. That is a static policy decision, not something a probe can change, but normal routing still enforces the allowlist on every request. Readiness is different, and for Claude Code the version check is tri-state, not pass/fail. Basic readiness needs the CLI installed, signed in, and passing the completion receipt; a conclusive below-floor result (a clean version probe reporting a version under 2.1.241) blocks readiness outright. An indeterminate boundary read (the version probe exited abnormally but still printed a parseable version, even one under the floor) can remain selectable with reduced capabilities, while a CLI that reports no parseable version at all fails the completion receipt and is not selectable. The 2.1.241 boundary itself is enforced separately, only on tool-bearing runs. Only the completion receipt is cached, for 24 hours; the version and sign-in checks run every time and are never skipped just because a cached receipt exists. That 24-hour TTL governs the settings route's own smoke-probe reuse, not how long a selected runtime stays trusted for real work. Settings persists capability evidence, alongside the executable's identity (path, version, and binary fingerprint), only when a runtime is selected, auto-selected as the sole ready runtime, or re-probed while it's already the selected runtime. Probing a runtime that isn't currently selected is diagnostic only: it shows the CLI's readiness in Settings but leaves no evidence behind for normal AI routing to reuse. Normal AI routing reuses persisted evidence for as long as the current executable's identity still matches what was probed, with no separate check of how old the probe is and no repeated sign-in. Probing the currently selected runtime again from Settings replaces its persisted evidence and timestamp. Running a real workflow reapplies more checks on top of that: normal AI routing rechecks supported, every runner validates the runtime's capabilities before it will spawn anything, the permission boundary is rebuilt for that call, tool-bearing runs repeat the version-floor check, and the binary fingerprint (the executable's identity) is checked immediately before the workflow runs.

The other nine

The rest of the registry exists for diagnostics, not workflows. Detecting a CLI is filesystem-based: CareerRat looks for the CLI's installed executable on disk and never calls out over ACP to find it. Gemini CLI, OpenCode, GitHub Copilot CLI, and Hermes Agent do have a real ACP transport wired in, built for future acceptance testing, but CareerRat doesn't use it yet: settings shows each of the four as detected_unverified and never runs the readiness probe against them, so there's no probed readiness state to see. Their acceptedCapabilities is set to the no-workflow capability set, but that's policy metadata describing what a runtime would be allowed to do, not the reason these four are excluded today. What actually keeps every one of the nine out of real work, on the settings route and CareerRat's normal AI routing, is the same supported: false flag from the registry. Qwen Code, Antigravity, Amp, Goose, and Droid have no protocol adapter at all. Asking CareerRat to run work through any unsupported runtime on those two paths is rejected before an invocation is ever built. The settings route checks availability before support: an unsupported runtime that isn't even installed returns RUNTIME_NOT_AVAILABLE; RUNTIME_NOT_SUPPORTED only comes back once the runtime is detected and available. Normal AI routing returns a generic route-failure result that callAI throws as a plain, uncoded error; workflow callers that catch it, such as the skill runtime, chat runtime, intake route, and workspace-agent route, may then label it NO_AI_ROUTE themselves. RUNTIME_COMPLETION_UNSUPPORTED is a different, lower-level check: it comes from a runner's own capability assertion when something asks it to spawn a runtime directly, bypassing normal routing. That guarantee covers those two paths, not every way CareerRat's own code can call a runtime; the lower-level runner functions don't check supported themselves.

Building the four gates for a new engine is real, deliberate work: a permission boundary mapped onto that CLI's actual sandbox flags, plus a completion receipt loop that verifies the CLI can complete a bounded, structured request using the configured invocation. The permission boundary itself is established by the adapter implementation and its acceptance tests, not by the receipt; the receipt shows the CLI can finish a scoped task, not that it honors every sandbox flag. A CLI that can read a markdown wrapper and be spawned bare is a different, weaker claim. CareerRat's docs try not to blur the two.

On this page