Skip to content

Admission Gates

Central Agentic Ops admits a run only when its checked-in control policy authorizes the workflow identity and requested limits. Admission happens in the gh-aw pre-activation job, before activation and before any agent executes.

The hosted Go server also supports the separate gh-aw cooperative activation-admission protocol at POST /api/admission. That optional OIDC-authenticated cost gate does not replace the control-policy checks described here and cannot enable campaigns, widen target scope, or grant writes.

Once that authority is deployed and the repository is explicitly enrolled, configure the participating workflow’s repository variables with the native CLI:

Terminal window
# Preview without changing variables.
gh cao setup admission --repo OWNER/REPO --url https://cao.example.org/api/admission --dry-run
gh cao admission setup --repo OWNER/REPO --url https://cao.example.org/api/admission
gh cao admission status --repo OWNER/REPO --json
gh cao admission disable --repo OWNER/REPO --dry-run

gh cao setup admission and gh cao admission setup configure the same non-secret variables; no workflow secret is needed for OIDC authentication. Setup and disable apply changes unless --dry-run is supplied. Setup uses the URL as the default audience and defaults outages to block. After initial setup, omit --url to reuse the target’s configured server or the current control repository’s configured server for fleet rollout/recovery. See Set up the admission gate for authority preparation, local readiness checks, a live canary, rollout, and troubleshooting. --audience and --on-outage ignore are explicit overrides. Status includes inherited organization settings; disable shadows them with an empty repository URL instead of deleting the override. These commands neither deploy the authority nor enroll repositories, compile workflows, or alter CAO policy.

For bulk rollout, replace --repo with one of these mutually exclusive selectors:

Terminal window
gh cao setup admission --all-repos --url https://cao.example.org/api/admission --dry-run
gh cao setup admission --org acme --org engineering --url https://cao.example.org/api/admission --dry-run
gh cao setup admission --enterprise company --url https://cao.example.org/api/admission --dry-run

Remove --dry-run to apply. --all-repos uses the validated local CAO global scope: exact repository allowlists, or the visible repository inventory of each allowed organization. Personal owners require exact repository lists. --org provisions variables with visibility all, including future repositories. Enterprise rollout enumerates organizations and configures each organization’s variables; there is no enterprise variable scope. Existing repository overrides still take precedence. Enterprise read access does not substitute for organization Variables write permissions.

status and disable accept the same selectors. Bulk preflight reads all targets before mutation; write failures report completed targets and stop without automatic retry or rollback. The limit is 1000 targets per command.

trigger -> pre-activation admission -> authorized-run precompute -> activation -> agent
| denied | blocked
+----------------------+-> no agent execution

Admission queries the GitHub rate-limit API and uses the returned core limit, remaining capacity, and reset time directly. It does not read or write a repository variable to coordinate capacity decisions across runs.

The former CAO_GITHUB_API_GATE repository variable is not an authority or integrity input and is no longer used. Capacity is checked directly with the job-local credential, so there is no shared mutable gate or concurrent gate writer.

There is no .github/cao/src/report.mjs in the current control runtime. Admission reporting is emitted by .github/workflows/shared/control.mjs; dashboard reporting is the separate modular pipeline under dashboard/report/.

The shared control component keeps one canonical runtime under .github/workflows/shared/. The exact-github.workflow_sha shared checkout contains that runtime and .github/workflows/cao.json; authorized runs execute precompute from the same checkout.

CheckAdmitted when
Runtime revisiongithub.workflow_sha is an exact commit and the policy and both CAO modules are readable at that revision.
Policy documentThe JSON has supported keys, types, ranges, unique names, no duplicate keys, and no GitHub Actions expressions.
Control planecontrol-plane exists.
Workflow identityThe workflow declares orchestrator, worker, or standalone; workers declare an exact worker identity, while standalone workflow slugs must match the actual compiled workflow reference.
CampaignThe campaign, or standalone workflow under control-plane.workflows, is declared and not disabled.
WorkerA worker is declared under that campaign and not disabled.
Target inputA supplied target_repo uses exact owner/repository form. Scope and access are checked later during precompute.
Mode inputreview or live does not exceed the checked-in ceiling; request-only debug passes the manual worker and human authorization gates below.
Run limitsmax_repos and rollout_percent are valid and do not exceed checked-in policy.
GitHub API capacityThe credential used for precompute has enough primary REST API capacity for the run.

A manual dispatch can narrow a run, such as changing an authorized live run to review or reducing max_repos. It cannot promote mode, add scope, enable a standalone workflow, campaign, or worker, or increase a limit.

Standalone workflows use these same gates without campaign records or worker discovery. Precompute admits a deterministic repository set through the shared scope, inventory, percentage, and repository limits; each candidate carries its effective mode and output route. The workflow executes its own mission directly on that set and cannot discover additional targets or dispatch work. Required inventory failures prevent activation. See standalone workflow configuration.

debug is accepted only for a directly launched workflow_dispatch worker, not an orchestrator, scheduled run, or bot dispatch. Admission verifies the authoritative event inputs and human sender, rejects a different rerun actor, and checks that the sender still has write, maintain, or admin permission on the control repository. Missing event or permission evidence denies the run. Precompute repeats that check before producing the worker handoff.

Debug retains exact-SHA policy validation, campaign and worker enablement, target scope, request limits, credential requirements, and API-capacity checks. It replaces only dispatcher-envelope requirements: correlation ID, central repository, and originating-run URL inputs must be empty. The output repository must equal the explicit target; no review destination is resolved because gh-aw stages every declared output. Debug cannot be persisted as a policy mode or dispatched by an orchestrator. See Local Worker Debugging for launch commands, staging behavior, and the local-origin limitation.

workflow_dispatch authenticates the caller as the GitHub App bot that holds the write-App credential. Allowlisting that bot preserves safe-output worker dispatches, but its login alone is not cryptographic proof of a particular orchestrator run, App installation, or dispatch envelope: a holder of the same App credential can submit equivalent workflow-dispatch inputs directly.

CAO therefore treats the write-App credential, its control-repository secret, and its selected installations as part of the trusted control-plane boundary. A worker independently fails closed unless the policy at its exact workflow SHA declares its campaign and worker, keeps both enabled, accepts the requested mode and output route, and accepts the target owner and any exact repository allowlist. The target repository cannot widen, narrow, or veto this authority through its own files. These checks prevent a dispatch from widening policy, campaign, target, or mode, even when the caller has the allowlisted bot identity.

The correlation ID and control-plane run URL provide audit linkage only; they are dispatch inputs and cannot establish provenance by themselves. A deployment that requires proof that only a particular orchestrator run issued a worker dispatch needs a signed, replay-resistant envelope generated outside the agent-visible dispatch inputs (or a GitHub-provided source-run attestation). Do not treat the App login, installation access, or a matching run URL as a substitute for that stronger guarantee.

Admission is deliberately lightweight and repository-local. Once admitted, .github/workflows/shared/control.md runs deterministic precompute checks that need credentials, repository metadata, inventory, or usage evidence.

AdmissionAuthorized-run precompute
Validates policy and workflow identityResolves repository inventory and allowlists
Rejects disabled or undeclared campaigns and workersVerifies target and review-repository access
Prevents manual inputs from widening policyBinds live workers to centrally authorized targets and output routes
Computes policy ceilingsApplies repository, rollout, and dispatch limits
Uses only the control repository revisionConfirms installed worker workflow availability and binds output routing

Failure in either phase prevents agent execution. Admission denial skips activation; precompute fails closed when an authorized run cannot establish a required remote fact. Successful precompute uploads only control-precompute.json; the agent job restores and validates that non-secret artifact before checkout or model invocation.

Setup creates one atomic control-plane revision:

  1. Install the gh-aw campaign from an immutable CAO tag or commit.
  2. Verify that campaign installation copied .github/workflows/shared/control.mjs and .github/workflows/shared/policy.mjs.
  3. Declare the installed campaign and its worker-to-workflow mapping in .github/workflows/cao.json.
  4. Commit the workflows, generated locks, campaign records, and policy together, then push before running the campaign.

The Bash installer installs the explicit CAO root package and creates the consumer-owned policy. The package provides one runtime copy under .github/workflows/shared/. Controlled workflows receive it through their existing exact-SHA shared checkout; they do not fetch another copy from the CAO repository. Follow Set Up CAO to bootstrap the repository and configure its exact scope interactively.

Control-plane installation does not declare an operational campaign in consumer-owned policy or grant admission. The CAO setup procedure and checked-in control policy own those decisions.

The Configuration Reference defines every policy field. The phase that uses each group is:

ConfigurationAdmission effectPrecompute effect
control-plane.campaigns and workersDeclares and enables the exact workflow identity.Resolves installed worker workflow paths.
control-plane.workflowsDeclares and enables the exact standalone workflow identity.Admits its bounded repository set without worker workflow discovery.
mode, target mode, and worker max-modeEstablishes the maximum mode a dispatch may request.Re-resolves the central ceiling before worker execution.
max-repositories and rollout-percentRejects a wider manual request.Bounds selected repositories.
scopeValidates and returns the configured owners and repositories.Filters inventory and rejects out-of-scope targets or review destinations; workers also reject targets outside a configured exact repository allowlist.
inventoryValidates scan, cell, and batch limits.Performs bounded discovery and deterministic batching.

Open the run summary and expand Central Agentic Ops admission. An authorized run names its campaign and role, and every check in the list is marked ✅. A denied run records a reason, marks every check before the failing one ✅, marks the exact failing check ❌, and leaves activation skipped. The ❌ marker identifies which row of the table below to consult; later checks are left unmarked because admission stopped before reaching them.

ReasonCheck marked ❌Configuration or setup to check
Cannot read or execute CAO runtimeRuntime revisionVerify that the CAO root package installed .github/workflows/shared/control.mjs and policy.mjs and that both exist at the workflow revision.
control policy validation failedPolicy documentValidate policy keys, types, ranges, unique names, and expressions in .github/workflows/cao.json.
control-plane-absentControl planeAdd control-plane to .github/workflows/cao.json.
role must be orchestrator, worker, or standalone, worker identity is required, worker identity is forbidden for orchestratorsWorkflow identityFix the role and its workflow or worker identity.
standalone workflow identity must match GITHUB_WORKFLOW_REFWorkflow identitySet the standalone import’s workflow to its exact compiled filename slug.
workflow-undeclared, workflow-disabledCampaignReview the standalone entry under control-plane.workflows and its enablement.
campaign-undeclaredCampaignAdd the installed campaign under control-plane.campaigns.
campaign-disabledCampaignReview the campaign, then remove enabled: false when it is safe to resume.
worker-disabled, unknown worker: <campaign>/<worker>WorkerReview the worker, then remove enabled: false or declare it under control-plane.campaigns.<campaign>.workers when it is safe to resume.
target_repo must use owner/repository formTarget inputFix the target_repo manual input to the exact owner/repository form.
safe_output_mode exceeds checked-in policy, safe_output_mode must be review or liveMode inputNarrow the requested safe_output_mode, or raise the checked-in campaign, target, or worker mode/max-mode ceiling.
debug ...Mode inputLaunch a worker manually as a write-authorized human, use an in-scope target as the output repository, and omit dispatcher-envelope inputs. Inspect the exact reason for missing event or permission evidence.
max_repositories exceeds checked-in policy, rollout_percent exceeds checked-in policy, or an integer-range messageRun limitsNarrow the requested max_repos/rollout_percent, or raise the checked-in max-repositories/rollout-percent.
github-api-capacity-insufficient, github-api-capacity-unavailableGitHub API capacityFollow the remediation guidance in the run summary and wait until the reported reset time before retrying.

Fix the checked-in setup or policy, commit and push the new revision, then start a new run. Do not bypass admission by editing a generated .lock.yml file or by widening manual inputs.