Skip to main content

Enforcement status

See where your posture reports — and where it prevents.

The cross-layer status surface gives an honest picture of the enforcement posture your configuration currently exposes.

What status reports

Measured 2026-09-30 on coderifts@8.6.8, with no API key, in an empty git folder: coderifts status exits 0 and prints three local lines. Runtime PREVENTS (host wrap fail-closed; CODERIFTS_ADVISORY unset), Merge REPORTS (MERGEGATE_ENFORCE=not true) is what coderifts status printed that day. It is not the live check conclusion: check_run 111270526393 concluded failure. Deploy PREVENTS (deploy-gate fail-closed). The 2026-09-18 sentence that said both commands stop at no API key is withdrawn. In a folder with nothing wired, the three lines describe the packages' defaults, not this repository — the measured line is coming.

coderifts status is a read-only report. These three lines do not write to GitHub and do not change branch protection.

REPORTS

The result is visible while merge or deploy can still proceed.

PREVENTS

The action physically cannot proceed on that path.

coderifts status owner/repo
coderifts status --repo owner/repo --json

What enforce does

coderifts enforce closes enforcement gaps by chaining existing setup commands: Merge → setup-required-check; Runtime → hook install (apply only) plus agent-guard guidance; Deploy → deploy-gate CD-step guidance; Content → guidance only.

Dry-run by default

--apply is required to mutate. The command does not silently flip enforcement, does not bypass underlying command safety, does not label Runtime or Content as server ENFORCING, and does not upgrade UNKNOWN.

coderifts enforce owner/repo
coderifts enforce --repo owner/repo --apply

The reporting-to-enforcement bridge

status shows the posture the endpoint measured. enforce is the dry-run-first setup path for closing gaps. The gates enforce; these commands surface and configure the posture.

Make the check block a merge

A CodeRifts check appears on a pull request as soon as the App is installed. It does not block anything at that point. Four conditions have to hold together before GitHub will actually stop a merge; miss any one and the check is advisory, which looks identical on the PR page.

  1. Branch protection requires the context by name. Read 2026-10-04, the check-run name fields on coderifts/demo pull request 4, head a81e6092fea0ca82696a6d252441afde4117f2fa, are CodeRifts / issuer (app id 2860592, check_run 111270526393, conclusion failure) and CodeRifts / contract-gate (app id 15368, check_run 111270560848, conclusion failure). No check-run on that head is named with an em dash. A different string does not match.
  2. Each context is bound to the app that posts it, not just to the name. Under name-only matching (app_id: null) any user with write access can post a success status under that name and open the merge. Bind CodeRifts / issuer to app id 2860592 and CodeRifts / contract-gate to app id 15368.
  3. An analysable schema is present. With no spec to read, the check reports neutral — and GitHub does not treat neutral as a failure. A required check can stay green while looking at nothing.
  4. enforce_admins is on. Without it an administrator walks straight past a red check, and the merge happens anyway.

Binding by app_id is what the checks[] form of the branch-protection API is for:

gh api -X PUT repos/OWNER/REPO/branches/main/protection/required_status_checks \
  --input - <<'JSON'
{
  "strict": true,
  "checks": [
    { "context": "CodeRifts / issuer", "app_id": 2860592 },
    { "context": "CodeRifts / contract-gate", "app_id": 15368 }
  ]
}
JSON

The App posts CodeRifts / issuer. GitHub Actions posts CodeRifts / contract-gate. On the head above both concluded failure, so this page does not say the issuer conclusion is neutral. What each path does separates the two.

When our API is unreachable

Measured 2026-10-02 with our API unreachable, timing out, or answering 5xx: on the enforcing profile, a head commit that carries a valid signed receipt for its own diff still passes offline without calling us, and every other case fails closed (the contract-gate check concludes failure); if the CodeRifts App cannot post its `CodeRifts / issuer` run, a repository that requires that context stays blocked as "Expected — waiting for status", GitHub's documented behavior for a required check that never reports.

Everything above is GitHub, and it is the “how to make it block” side. Below is the other side, for all three providers we read: what a VERIFIED result means there, and what it cannot mean. It is generated from the CLI’s own statements, so this page cannot drift from the tool that produces the verdict.

Two app ids appear below, and they are not the same control. CodeRifts / issuer is the CodeRifts App, app id 2860592. CodeRifts / contract-gate is GitHub Actions, app id 15368, shared by every workflow in the repository. Both concluded failure on the demo head named above. What each path does separates them per path.

What VERIFIED means, per provider

GitHub

VERIFIED on GitHub means a required status check named contract-gate is bound to a specific app id, on a surface that blocks the merge — either classic branch protection's required_status_checks.checks[], or an active repository ruleset that targets this branch. Only that app can satisfy the check; a passing status posted by anything else does not clear it.

What it cannot mean:

  • It does not identify the WORKFLOW. The bound id is normally 15368, the GitHub Actions App, which is shared by every workflow in the repository — anyone who can write .github/workflows can post a passing check under it. A required workflow pinned by sha is what narrows this to specific bytes.
  • It does not bind a repository admin. With enforce_admins off, an admin merges past the check.
  • It is a configuration fact, not a run: it says the gate is set up, not that any particular pull request was gated by it.
  • It does not cover bypass actors, which are not readable from the ruleset document.

GitLab

VERIFIED on GitLab means the merge was gated: the protected branch restricts who may merge and forbids force-push, the project requires all status checks to pass, and an external status check passed for the merge request HEAD SHA.

What it cannot mean:

  • It does not identify WHO satisfied the check. GitLab external status checks bind to the merge request HEAD SHA and carry no issuer identity — the API records that a check passed, not which integration passed it. Any actor holding a token permitted to post external status checks can satisfy this control.
  • There is no GitLab equivalent of the GitHub app-id binding. This is a platform limit, not a configuration gap: no setting on GitLab closes it.
  • External status checks are a GitLab Ultimate feature. On a lower tier the project cannot express a merge-blocking check at all, which we report as UNSUPPORTED_PLAN rather than as a misconfiguration.

Bitbucket

We do not currently issue VERIFIED for Bitbucket from configuration alone. Branch restrictions are readable and we report exactly which are present; the result is capped at UNVERIFIABLE until a behavioural canary observes the provider actually refusing a merge.

What it cannot mean:

  • A green build is not an unforgeable gate. require_passing_builds_to_merge gates on build statuses, and any actor with repository write can overwrite a status by posting a new one on the same commit.
  • The control that WOULD be unforgeable — a required custom merge check, which is a Forge app and Premium-only — exposes no API for reading its configuration, so whether it is present and required cannot be determined by reading.
  • Because of the two limits above, a configuration read on Bitbucket can never rise above UNVERIFIABLE. Only the negative canary can settle it.

Honest bound

Status reflects configuration, not a guarantee. A layer shown as preventing depends on repository and host configuration. Prevention holds inside the wired boundary; an unwired surface can be bypassed.