Retiring a worker — RETIRED_WORKERS
A worker whose account is unfunded or ineligible is not the same as a worker with no credential,
and Cyberine used to have no way to say so. An out-of-balance account still holds a VALID key, so
every availability probe passed, the worker was registered, dispatched, and only then failed on the
provider's billing error — burning a dispatch slot on every run, indefinitely. RETIRED_WORKERS in
src/core/workerProfiles.ts is the operator's recorded answer to that.
Retired (mirrors RETIRED_WORKERS; the source is the authority):
| Worker | Provider | Why |
|---|---|---|
loop-kimi-build, opencode-kimi | Moonshot / Kimi | account out of balance |
loop-minimax-build, loop-minimax-direct, mmx-text-build | MiniMax | 402 insufficient balance (1008) |
gemini, loop-gemini-build | free-tier client revoked (IneligibleTierError), no funded paid tier | |
loop-muse-build (2026-09-05) | Meta Model API | 402 billing_not_configured — key valid, no payment method |
opencode2-deepseek (2026-09-06) | DeepSeek (via opencode2) | account out of balance — 402 Insufficient Balance |
opencode-deepseek, opencode (default-compare planner) (2026-09-07) | DeepSeek (via opencode) | operator is dropping the opencode binaries; cyberine run replaces opencode as the one-command edit tool (ks4sf) |
The gate has THREE independent entry points, because no single one covers the fleet:
availableBuildWorkerIds() builds the candidate id list from scratch, routeTask() ranks
candidates through sortedAvailable(), and buildDefaultAdapters() filters the assembled adapter
list just before returning. cyberine doctor reports a retired worker as [WARN] <id>: RETIRED <date> — <reason> instead of the false [PASS] installed, logged in it used to print, and a
provider whose workers are all retired says so on its own line while still reporting the key as
present — those are two different facts and it now states both.
Bringing one back is deleting its entry from RETIRED_WORKERS. Nothing else is removed: the
WORKER_PROFILES entry, the adapter, the model-catalog rows and the pricing rows all stay, and
selftest:retired-workers asserts that reversibility explicitly so a future "cleanup" cannot
quietly delete the profiles and make the decision one-way. Two module-load guards back it up: a
retired id may not appear in CHEAP_BUILD_FLEET (it would be dispatched by cyberine run --cheap
and fail on billing), and every id in RETIRED_WORKERS must be a real worker, so a typo cannot
silently retire nothing.
npm run selftest:retired-workers # offline; asserts all three gates + the reversibility contract
cyberine doctor # shows each retired worker and whyKnown limitation / deferred work: retirement is a source-code constant, not runtime config —
there is no cyberine fleet retire <id> command, and changing it needs an edit plus a restart.
The provider-status line maps providers to their workers by substring with a small alias table
(moonshot -> kimi, google -> gemini), because WORKER_PROFILES declares no
provider-to-worker edge; that mapping is cosmetic (nothing dispatches off it) but a future worker
whose id does not contain its provider name would be missed there. Retirement also does not touch
fleet.json — an explicit CYBERINE_BUILD_FLEET naming a retired worker still drops it, but the
config file's own validation will not warn you that the entry is now inert.
Spawned CLI workers and PWD
childprocess.spawn() sets a child's working directory but copies PWD from the parent environment verbatim — a shell updates PWD on every cd, Node does not. So a CLI worker spawned into a git...
The web UI — cyberine web (browser fleet monitor + chat)
cyberine web starts a dependency-free HTTP server (Node's built-in http, no framework) that serves a single-page browser UI mirroring the fleet TUI plus the chat planner. Same data as cyberine...