Skip to content

Orgabot is in private alpha. Request access, or join our Discord.

← All documentation
The organization

Running CI locally without overloading your Mac

Admit CI jobs alongside missions, queue them fairly, limit them, and stop a busy or offline runner from moving cost to a paid provider.

Last updated

Running CI on your own machine saves hosted minutes, but only if it does not take the machine down or quietly push cost to another provider. Orgabot runs CI jobs through the same admission checks missions use, and treats paid fallback as something you approve, never a default.

How a CI job is admitted

Every CI job asks for a slot before it starts. The decision reads:

  • Host pressure: load, memory, kernel memory pressure, swap growth, and the memory watchdog. Heavy jobs wait as soon as the host is contended; light control work keeps going until pressure is elevated.
  • Capacity after your reserve: memory, CPU and disk are packed against what is left after the reserve you configured, minus everything already admitted (missions included).
  • CI budgets: concurrent processes, concurrent compiler and test workers, Docker VM headroom, and disk and cache space.
  • Safety thresholds: a heavy job is refused if admitting it would push memory use, CPU load, or free disk past the configured ceilings.

Missions see CI the same way: a mission launch counts the resources held by admitted CI jobs.

One build/test budget for missions and CI

A heavy CI job runs on the same machine as a mission's verification, so the two share one set of build/test slots: the host verify slots you set with orgabot verify slots or a scheduler preset.

  • Builds, tests and container builds take a full slot, like a mission's full verify. Lints, signing and deploys take a light slot. Control work and artifact transfer take none.
  • A CI job waits in the same fair queue as missions. While a mission's verify holds the slot, the job says so: "held by mission ...".
  • A mission's full verify likewise waits while a CI job holds the slot.

So with the default of one full slot, a mission's test suite and a CI test run never run at the same time.

Slots and fair queues

There are two kinds of slots:

SlotWorkDefault
Controlorchestration, polling, artifact transfer4
Heavybuild, test, lint, container build, signing, deploy1 per 8 cores

Each target (runner) also has its own bound. Waiting jobs take turns by organization first, then project, so one busy project cannot hold the machine, and a job that has waited a long time is promoted.

Under pressure

Only work that can safely stop is stopped:

  • Tests, lints and builds are re-runnable: under critical pressure the lowest-priority one is cancelled and requeued. This is not counted as a retry.
  • A container job that supports it is paused; a job that supports checkpoints is checkpointed.
  • Signing and deployment are never frozen or killed. They finish while new work waits.

Each job runs with limits that match what it was admitted with: build and test parallelism, Node heap size, lower scheduling priority for heavy work, and Docker CPU, memory and process limits.

Spend, fallback and retries

Remote CI spend is reserved before a job is dispatched, against your organization budgets (see Budgets, quota, and analytics) and the CI ceilings, then reconciled with the actual cost afterward.

  • The reservation is priced at the job's timeout and includes artifact storage, data transfer and provider fees, not just runner minutes.
  • Missing prices are priced at the most expensive known rate plus a margin. If no price is known at all, the paid dispatch is refused. Stale prices are padded, or refused if you set refuseStalePricing.
  • A busy or offline local runner queues. A paid provider is used as a fallback only when you list it in approvedPaidFallbacks and the spend fits every hard ceiling. The default fallback budget is $0.
  • A test failure is the job's answer. It is never retried and never moved to a fallback, so it cannot spend the fallback budget. Only infrastructure failures are retried, up to maxRetries.
  • Routing modes (explicit, preferred, optimized) all respect trust, data-export rules and spend ceilings. Optimized routing uses observed cost and duration, and falls back to the fixed preferred order, saying so, when there are fewer than 5 samples per target.
  • The observed history comes from Orgabot's own record of finished CI jobs over the last 30 days: local runs and settled remote dispatches. A test failure counts as a run that reached an answer, not a failure of the runner. Only infrastructure failures lower a target's success rate, so failing tests never push work to a paid provider. Cancelled jobs are not counted.

Commands

orgabot ci status            # slots, queue reasons, pressure, reserved/actual spend, recent decisions
orgabot ci run --project my-app --kind test -- npm test
orgabot ci route --org acme --kind test --expected-min 8 --max-min 30
orgabot ci cancel <job-id>   # dequeue, release the slot, release reservations
orgabot ci policy show
orgabot ci policy set '{"routing":{"approvedPaidFallbacks":["gha"]},"spend":{"fallbackDailyCeilingUsd":5}}'

The dashboard's CI/CD view shows the same information under Local CI admission: CI and shared build/test slots, host pressure, reserved, actual and estimated spend, the paid fallback setting, the routing evidence, each waiting job with its reason, and recent decisions with what was decided and why. It reads /api/ci/optimization. The same spend, queue reasons, host pressure and escalation decisions also feed the hybrid view's Execution metrics, so reserved, estimated and reported cost there are this budget's figures. Decisions are also kept in ~/.orgabot/ci/decisions/.

Current limits

orgabot ci run executes on the local runner. Dispatching to a remote provider needs that provider's execution adapter, which is tracked separately; ci route already shows what such a dispatch would choose and reserve.