Skip to content

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

← All documentation
Reference

CI/CD execution: adapters, capabilities, and preflight

Register Docker hosts, native workers, and GitHub Actions runners, verify what each can really do, and see why a CI target runs where it does.

Last updated

What this is

A CI/CD target (a test suite, a macOS build, a Windows installer) has to run somewhere that can actually build it. Orgabot keeps a registry of named execution instances, discovers what each one can really do, and runs a preflight that picks where a target should run and explains why every other instance was passed over.

This is separate from the AI workers that run missions. An execution instance runs a project's own build, test, and packaging commands. It never runs a model.

Adapter families and locations

KindWhere it can be registeredWhat it is
dockerworkstation, remote_hostA Docker engine running jobs in containers
native_workerworkstation, remote_hostA Mac, Linux, or Windows machine running jobs on the host itself
local_actionsworkstationact, which runs GitHub Actions jobs locally in Linux containers
github_actionsgithub_hosted, self_hosted_poolGitHub Actions, either GitHub's own runners or your self-hosted pool

GitHub-hosted runners and a self-hosted pool are separate locations, so a policy can prefer or exclude one without the other. You can register as many named instances of each kind as you like.

orgabot cicd adapters register local-docker --kind docker --location workstation --image node:22
orgabot cicd adapters register mac --kind native_worker --location workstation
orgabot cicd adapters register winbox --kind native_worker --location remote_host --ssh ci@winbox
orgabot cicd adapters register gh-windows --kind github_actions --location github_hosted \
  --runner-label windows-latest --repository you/app --connector <org>/<connector-id>

A GitHub Actions instance names the GitHub App connector whose health stands for its authorization. The registry never holds a credential.

--ssh must be [user@]host, where host is a hostname, an IPv4 address, or a bracketed IPv6 address such as ci@[fe80::1]. To use a non-default port, add a Host entry to your ssh config and register that alias. --image must be a Docker image reference, [registry/]name[:tag][@sha256:digest], and --repository must be owner/repo. Anything else, including a value that starts with -, is refused when you register it and when Orgabot reads executionAdapters.json, so a hand edit cannot turn one into a command-line option.

A label is not proof

--label windows,x64,msi (or the same flag repeated, --label windows --label x64) is recorded and shown, but it never satisfies a requirement. Capabilities come only from a probe:

orgabot cicd adapters probe            # every instance
orgabot cicd adapters probe winbox     # one

A probe reads versions and inventory and changes nothing: the OS and CPU architecture, which architectures run natively and which only under emulation, toolchains such as Xcode, WiX, and Node, free capacity (from the same sample mission admission uses), signing identities, platform features, artifact transport, and price. For a GitHub-hosted runner, which is a fresh machine per job, the evidence is GitHub's published image catalog for that runs-on label.

A Linux container on an Apple silicon Mac is reported as native arm64 with x64 emulated. It is never reported as a native x64 machine, and never as a Windows machine.

Preflight

Describe what the target needs in a requirements file:

{
  "target": "premail:windows-installer",
  "os": "windows",
  "arch": "x64",
  "features": ["windows_installer"],
  "trustClass": "trusted",
  "estimatedMinutes": 12
}

Then:

orgabot cicd preflight --requirements windows.json

Preflight reads the registry and the last probe of each instance. It runs no job code and probes nothing (add --refresh to probe first). It prints the selected instance with every requirement it satisfies and the evidence for each, a cost estimate, the fallback order, and for every other instance the reason it was passed over and the command that fixes it.

Reasons fall into distinct groups, so a closed Docker Desktop is never mistaken for a machine that cannot do the job:

GroupMeaning
healthNever probed, probe older than the freshness window (one day by default), unreachable, or unidentifiable. The instance may well be capable.
credentialsIts connector reports an expired, revoked, or paused credential.
capabilityA verified fact rules it out: wrong OS, x64 only emulated, missing toolchain, no signing identity.
trustUntrusted code may not run on your workstation or a shared self-hosted pool, and release jobs may not run on a shared pool.
limitsNo free slot, not enough cores or memory, or over the spend limit.
policyExcluded by the execution policy.
statusRevoked or disabled.

Explicit platform requirements

Some needs are never assumed. A job must name them under features:

  • windows_installer: building or testing an MSI needs the Windows Installer service on a native Windows host. No container qualifies.
  • native_ui_session: native UI tests need an interactive desktop session. No container qualifies.
  • gpu: needs a GPU the probe actually observed.
  • github_oidc: OIDC tokens exist only inside GitHub Actions.

Signing identities go under signing (for example apple_developer_id or windows_authenticode). GitHub-hosted runners carry none. When a signing identity is missing, preflight tells you to install it only on an instance that otherwise runs the target's operating system; a Linux container is never told to install a Windows or Apple identity it could not use.

Policy

--policy takes { "layers": [...] }, each layer an organization, project, or target scope with any of: selection (explicit, ordered, or optimized by cost), preference (instance names, kind:<kind>, or location:<location>), allow, deny, maxUsdPerRun, fallback (queue, fail, approval_required, or preauthorized), and maxProbeAgeMs. A lower scope may tighten a spend limit or narrow allow, never loosen them, and a field the organization lists in locks cannot be set below it.

At dispatch

The decision is checked again at dispatch time against current state. If the chosen instance was revoked, went unhealthy, or was reconfigured in between, dispatch refuses with the new reason instead of running on old evidence.

orgabot cicd preflight --requirements req.json --json > plan.json
orgabot cicd dispatch --plan plan.json --requirements req.json -- npm test

dispatch probes the selected instance again (an observation only), re-runs preflight with the same requirements and policy, and submits the job only if the instance still qualifies and its adapter can run jobs. On success it prints the run handle. On refusal it prints the reason and the fix, and nothing runs. Pass --no-reprobe to skip the fresh probe and rely on the last recorded one.

What is not here yet

Docker instances support the full run lifecycle (submit, inspect, logs, cancel, reconcile). Native workers, act, and GitHub Actions are discoverable and plannable today, but preflight marks them not yet dispatchable: running code on those locations waits on the organization trust and identity work, and a GitHub workflow run waits on the provider-neutral pipeline definition. Collecting outputs from a Docker run waits on the same pipeline definition's artifact declarations.

Managing instances

orgabot cicd adapters list                       # status, last probe, verified platform
orgabot cicd adapters disable winbox             # skip it without forgetting it
orgabot cicd adapters enable winbox              # re-probe before it can be selected again
orgabot cicd adapters revoke winbox --reason "decommissioned"
orgabot cicd adapters conformance                # which lifecycle steps each family supports

Revoking keeps the record and its name, so the history of where runs went stays readable. Register a replacement under a new name.