Skip to content

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

← All documentation
Reference

CI/CD conformance: adapter, security and parity tests

How execution adapters, policy, failure recovery and migrations are tested, what a migration must attach before cutover, and what was really exercised.

Last updated

Orgabot's CI/CD runtime places each pipeline target on an execution backend: local Docker, a native macOS/Linux/Windows worker, local GitHub Actions compatibility (act), or GitHub Actions. The conformance kit holds every backend, the policy resolver and every migration to the same rules, so a green result means the same thing wherever the work ran.

The kit lives in framework/src/cicd/conformance/. Run it with:

orgabot ci-conformance run

It exits 0 only when every suite passes and every adapter report is complete.

The three rules

Every case in the kit enforces these:

  • Failures stay failures. A non-zero exit, a timeout, a cancellation or a refused capability is never reported as success, and a late observation never changes a finished run. A test failure is never retried, and never sent to another provider in the hope that it passes there.
  • Uncertain stays uncertain. A run or side effect whose outcome could not be observed is unknown. It holds the pipeline until a provider lookup answers, and it is never retried blind.
  • No manufactured green. A success needs observed exit evidence for a pinned commit. A required check counts only from the expected reporter, for the exact commit, from a trusted workload.

Adapter conformance

Each adapter binds to one small port (ConformanceSubject in port.ts), and the same cases run against all of them:

AreaWhat is checked
Source pinningA branch or tag name is refused before anything launches; results and artifacts name the pinned commit
Commands and servicesExit codes are kept; later steps stop after a failure; a missing command fails; a service is reachable or the job is refused up front
Outputs, logs, artifactsOutputs are returned; logs are ordered and never contain a secret or its encodings; an artifact's digest matches its bytes
Timeouts and cancellationEach is its own terminal state, leaves no containers or processes, and a late cancel does not rewrite a finished run
RetriesInfrastructure faults are retryable; test failures are final
Capability mismatchAnother OS, an unsupported architecture or a missing feature is refused with the backend's own reason, and every reason is listed
Normalized failuresAn unknown run is unknown, never success; every observed result passes the honesty rules

Running it on real processes

orgabot ci-conformance run --binding native

--binding native runs the same adapter cases against this machine's real processes instead of a simulated backend. Each job is checked out from a real git repository at its pinned commit, each step runs in its own /bin/sh process group, timeouts and cancellation send real signals, and the infrastructure-fault case kills a real step with SIGKILL from outside Orgabot. Jobs get a clean environment, so nothing from your shell (including credentials) reaches them. This binding declares no emulation and no service containers, because it cannot verify either. It is the baseline for the native family. The native worker adapter (placement, worker leases, signing) binds to the same port when it lands.

The suite is proven to discriminate: each of the fourteen built-in mutants (an adapter that reports success on a non-zero exit, ignores cancellation, skips redaction, accepts a branch name, and so on) must fail it. A report also records complete: a binding that cannot inject faults gets not_exercised for those cases, which is never a pass.

Policy resolution

The policy suite checks organization, project and target resolution: inheritance is visible field by field, a narrower override can never weaken an organization restriction, a budget ceiling or an approval, an unreadable policy blocks, a project that still runs its own workflows resolves to them unchanged, and the CLI, dashboard, dashboard terminal, agent skills and mission verification all get the identical decision.

Adversarial cases

AttackDefense
Fork or PR privilege escalationTrust follows the code, not the event. Fork code gets no secrets, no side effects, no check authority, and never runs automatically on a persistent personal worker
Forged checksA commit status, another app's check, a check for another commit or from an untrusted run is refused
Hostile workflow inputsInputs must match a declared type and reach jobs only as environment variables; a step that splices an event or input expression into shell is refused
Poisoned cache or artifactAn untrusted cache never restores into a trusted job; bytes must match the recorded digest at every hand-off
Stale attestationRefused when the artifact, commit, pipeline definition, toolchain or builder changed, or when it is too old or dated in the future
Secret redactionRaw, base64, URL-encoded, hex and JSON-escaped forms are masked; a secret too short to mask is reported
Wrong-architecture outputThe architecture is read from the ELF, Mach-O or PE header, never from a label

Failure recovery and scenarios

Faults injected and checked: worker restart, controller restart, durable runtime restart, Docker loss, webhook loss, duplicate webhooks, provider token expiry, rate limits, disk exhaustion, memory pressure, a remote dispatch accepted with its response lost, and unknown signing or deployment outcomes.

Scenarios checked: a clean PR, a failing PR, a fork PR, a merge queue (evidence for the PR head never counts for the merge commit), a tag release, a scheduled run with the Mac offline (blocked, not skipped), and release recovery after a crash around publication.

The hybrid release follows the Premail reference flow: local verification and macOS/Linux builds, a GitHub-hosted Windows installer build with its existing signing identity, and one release join. The join needs an approval bound to the exact commit and artifact digests, verifies every artifact's bytes and architecture, and publishes once. Cancellation stops the hosted run and publishes nothing; rollback reverts exactly once, across restarts. The hosted runner is dispatched only after local verification passes, so it is never billed while waiting on local work.

Mixed mission and CI load runs through the same admission rule missions use, and must stay inside the host's memory reserve, CPU and concurrency ceiling while every item still completes.

Migration parity and cutover

Before a target moves from its existing pipeline to Orgabot, the migration attaches parity evidence:

orgabot ci-conformance run --family docker --report-out report.json
orgabot ci-conformance parity --migration mig-site --project site --target site \
  --old-coverage old.json --new-coverage new.json \
  --old-artifacts old/ --new-artifacts new/ \
  --rollback rollback.json --conformance report.json --out evidence/parity.json
orgabot ci-conformance verify-evidence evidence/parity.json

Cutover is blocked unless all of these hold:

  • Every required check still runs on every OS and architecture it ran on, still required. A renamed check needs an explicit entry in --renames.
  • Artifacts are equivalent. Plain files compare by content. Archives compare entry by entry, ignoring timestamps. Signed or notarized binaries compare by code with the signature excluded, and both signatures must verify with the same identity and keep notarization. Re-signing alone is recorded, not treated as a regression.
  • The target's adapter has a complete conformance report attached.
  • Parity ran twice with identical results.
  • Rollback restores the old triggers, was rehearsed, saw the old checks run again, and performed no production side effect.

Signatures are checked with codesign and xcrun stapler on macOS and osslsigncode for Windows binaries. If a tool is missing, that artifact is unverified, which blocks cutover. The evidence file is digest-stamped, so verify-evidence refuses a copy edited after it was written.

Test cost

TierRuns whenCost
DeterministicAlwaysSimulated backends and virtual time: no containers, runners, network or spend
Local liveORGABOT_CI_LOCAL_LIVE=1Real local backends on this machine
Hosted smokeORGABOT_CI_HOSTED_SMOKE=1 and ORGABOT_CI_HOSTED_SMOKE_APPROVAL=apr_...One Linux and one Windows cell, at most 8 billed minutes, no signing or publishing

No tier may publish, deploy, submit to a store, notify, or sign with a production identity.

What was exercised versus simulated

Only the adapter suite's native-process binding runs on real hardware today. Everything else in the kit runs on simulated backends:

AreaExercisedWhy not real yet
Adapter suite: native-process bindingReal hardware, every routine test runExercised
Adapter suite: Docker, native worker, act, GitHubSimulatedThe real adapters are not merged yet, so there is nothing to bind the suite to
PR, merge queue, tag, schedule, release recoverySimulatedThey run on the kit's reference reconciler until the production pipeline controller and check reporting land
Injected faultsSimulatedReal Docker loss and real worker or runtime restarts need the adapters and controller first
Hybrid releaseSimulatedA live run needs the GitHub-hosted Windows dispatch adapter, Premail's signing identity and an approved hosted smoke run
Signed binary comparisonFixture headersReal notarized apps and Authenticode-signed installers have not been compared yet
Mixed stressModeled hostUses the real admission rule, not real processes

EXERCISE_LEDGER in costBounds.ts is the source of this table, and a test fails if a new scenario is added without an entry. Update it only after a real run, and record where that run's evidence lives.