Skip to content

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

← All documentation
Using Orgabot

CI routing: local test results as GitHub required checks

Run CI targets on your Mac for pushes, PRs, merge queues, releases and schedules, and report signed results as GitHub checks a branch can require.

Last updated

Orgabot can run a repository's CI targets on your Mac and report each result to GitHub as a check run. A protected branch can then require those checks the same way it requires a GitHub Actions job. Nothing changes for a repository until you configure it, and a repository you have not configured keeps running exactly what it runs today.

What gets routed

Every repository event Orgabot can act on becomes one normalized event: a push, a pull request, a merge-queue group, a tag or release, a manual run, a scheduled slot, or a mission's own verification. Events arrive by signed webhook, by a bounded reconciliation poll from the recovery sweep, from the local scheduler, from you, or from a mission.

Each event is matched against the targets you configure in github/ci/routing.json under the Orgabot home directory. For every event kind, a target names exactly one owner:

  • orgabot: Orgabot runs it. This owner must carry a recorded authorization (an approval id, who approved it, and when). An orgabot owner without one is refused when the file loads, and the whole file is then ignored.
  • legacy_actions: the existing GitHub Actions workflow keeps running it, and Orgabot only observes.

An event kind a target does not name dispatches nothing.

One run per logical target

The same work often arrives more than once: GitHub redelivers a webhook, a push to a branch and the pull request's update name the same commit, the reconciliation poll sees a head the webhook already delivered, a tag push is followed by its release. Orgabot gives each piece of work a logical key (repository, target, the exact commit built, and the context) and claims it with an exclusive file, so exactly one observer runs it, even across separate processes.

Work that really is different gets a different key: a synthetic merge against a new base, a scheduled slot, or an explicit run you asked for.

Two loops are cut off before they start:

  • Legacy double dispatch. Before Orgabot runs a target for an event it owns, it reads each workflow listed in the target's legacyWorkflows at the exact commit. If one of them still triggers on that event, Orgabot does not run the target and leaves it to that workflow, so the work runs once. If the file cannot be read, Orgabot also holds rather than risk a second run. Once you remove the trigger from the workflow (or delete the workflow), Orgabot runs the target from that commit on. A tag-only push trigger does not block branch pushes, and a branch-only one does not block tags, matching how GitHub filters them. If a legacy run still starts for an event Orgabot owns, Orgabot records a conflict and shows it in orgabot cicd status.
  • Release recursion. A target that publishes releases never re-runs on a release cut by Orgabot's own bot identity or recorded as published by one of its runs.

Which commit is built and which is reported

Every run keeps two commits apart:

  • Reported: the commit GitHub evaluates required checks on, which is the pull request head, the merge-group head, or the pushed commit.
  • Built: the tree actually tested. It is the same commit, except for a pull request target set to prTestRef: "merge", which builds GitHub's synthetic merge commit. That result applies only to that base. When the base moves, the merge commit is new work.

Orgabot checks out the built commit in a fresh worktree and refuses to run unless the checkout is exactly that commit. A result from a remote workflow counts only when the workflow reports which commit it built. A workflow_dispatch run's own head commit is never taken as the commit it built.

Trusted results

A check turns green only from evidence. After a job's command exits, Orgabot signs a statement of the exit code it observed, bound to the repository, target, job, both commits and the context. The signing key is kept in Orgabot's secret custody and is never passed to the job. Before reporting, the gate re-checks every job against its signed evidence, so:

  • a run record edited to say "passed", or a worker's summary saying tests passed, reports as a failure;
  • evidence for another commit, another tree, or an older merge base reports as a failure;
  • a job skipped without the target declaring it not applicable reports as a failure.

Check names are stable: orgabot/<target>/<job>. Each state is reported distinctly:

StateGitHub conclusionSatisfies a required check
Passedsuccessyes
Cached with evidence (a signed pass for the same tree)successyes
Not applicable (declared by the target)skippedyes
Failedfailureno
Canceled (a destroyed merge group)cancelledno
Blocked (a fork pull request, a missing backend)action_requiredno
Unknown (no exit code observed, the controller stopped mid-run)action_requiredno

Fork pull requests are held as blocked until you run the exact head yourself with orgabot cicd run.

Orgabot's own merges

You do not have to finish the branch-protection migration before these results count. Every merge Orgabot performs (auto-merge after review, the dashboard merge, orgabot merge) first checks the CI gate when Orgabot owns a pull request or merge-queue target for the pull request's base branch. The gate reads only results for the exact commit about to land:

  • every required job passed with signed evidence: the merge goes ahead;
  • a required job failed, was canceled, is blocked or has an unknown result: the merge is refused;
  • no result has reported on that commit yet, or a run is still going: the merge waits, and the pull request stays open.

A repository with no routing config, or no Orgabot-owned target for that base, is not gated this way.

When your Mac is asleep or offline

Nothing reports the required checks, so GitHub keeps showing them as expected and the merge stays blocked. When the Mac returns, the recovery sweep's reconciliation pass routes every open pull request head, branch head and merge-queue head that has no result. A run that was in progress when the controller stopped is reported as unknown and run again.

Local jobs move to a remote runner only when the repository records an approved remoteFallback. A remote job that needs a local job is dispatched only after the local result exists, so a paid runner never sits waiting for your Mac.

Scheduled targets

Each scheduled target picks a catch-up policy for slots missed while the Mac slept:

  • skip_missed: run only a slot that is due now; record the others as missed.
  • run_latest: run the newest missed slot once; record the older ones as merged into it.
  • run_all: run up to maxCatchUpRuns of the newest missed slots; record the rest.

A slot older than maxLatenessMinutes is recorded as too late. Every slot is either run or recorded with a reason, and orgabot cicd status shows the count.

Moving required checks to Orgabot

orgabot cicd plan <owner/repo> --branch main builds a staged plan from the branch's live protection and the results Orgabot has actually reported:

  1. Introduce: the new checks report on real commits, without being required yet.
  2. Demonstrate: each new check has been seen passing and failing from Orgabot's App.
  3. Switch: the new checks are added as required and pinned to Orgabot's App id, so a status with the same name from any other identity cannot satisfy them. This needs the repository's protectionAuthorization and administration access.
  4. Retire: an old check stops being required only after every check that replaces it is required and demonstrated.

Every proposal is compared with the current protection. If it would remove a required check without a proven replacement, unpin a check from its App, turn off up-to-date branches or admin enforcement, lower required reviews, or drop a rule, the plan refuses it.

The Orgabot App does not have administration permission, by design. Any step it cannot take is printed as a human task with the exact settings path or gh api command to run.

Setup you must do

  • Give the Orgabot GitHub App the Checks: Read and write permission and accept it on the installation. Until then Orgabot cannot report, orgabot cicd plan lists this as a task, and the required checks stay expected.
  • Subscribe the App to the Merge group and Release events so merge queues and releases are routed.
  • Write github/ci/routing.json with the repository, its registered project, reportingAppId, and at least one target whose owners carry recorded authorizations. Check it with orgabot cicd status.

To route a project's own mission verification, the pre-merge gate and the CLI through these targets too, see One CI runtime for every entry point.