The merge gate

Installed by redline init on every onboarded repo, on either host. What it blocks on is set by the repository's rung on the enforcement ladder — except the security floor, which blocks everywhere.

What it checks

  • Merge-readiness checklist — the PR template's checklist section must be fully ticked; the gate fails loudly if the heading was deleted.
  • ADR for big diffs — large changes must link a decision record.
  • Deterministic policy — the rules a checker can decide, evaluated over the added lines with no model call.
  • Dependency review — new vulnerable or malicious dependencies block.
  • Diff secret scan — a SHA-pinned scanner over the PR diff, verified results only.

How much of it blocks

The process checks — checklist, ADR and policy — block according to the repository's rung. At observe and warn they report and never block; the findings are produced and recorded either way, because observe means “measured and not yet enforced” rather than “off”.

Dependency review and the secret scan are not on that ladder. They block at every rung, including observe.

Process vs security

The redline-exempt label, together with a recorded exemption naming a reason and an expiry, downgrades the process checks to warnings for a reviewer who accepts the trade-off. It does nothing to dependency review or the secret scan. Security checks can never be exempted.

Two hosts, two gate contracts

Reusable GitHub workflows report a check-run name derived from two job ids, so requiring the wrong string hangs every pull request in the repository. Azure has no such derivation: Redline defines its own contract, so nothing is derived from a job name and that failure mode does not exist on that host.

Azure Repos ignores a pipeline's YAML pr: trigger, so the gate pipeline alone runs nothing. redline init also registers a build definition for it and a Redline: gate build Build Validation branch policy that queues that pipeline on every pull request — without Build Administrator rights that registration is recorded as pending and the required status is written advisory, since no pipeline could publish what it would require.

HostGate contractRequired by the policy as
GitHubCheck-run name from the caller/called job idsredline-gate / gate
Azure DevOpsPR status, genre redline, name gateredline/gate

redline verify reads back the check names or statuses the host actually reported on a real pull request — not what a config file claims — before it will call the check name confirmed.

Adding a check of your own

Three ways, in the order you should reach for them. The first two need no change to Redline at all.

1. Run it beside the gate

A check that is yours alone — a licence scan, a bundle-size budget, a migration linter — is an ordinary workflow in your repository. Nothing about Redline stops you, and if you also want it to block, add its check name to the branch ruleset alongside redline-gate / gate. Redline's ruleset is applied by redline init, so add yours as a second ruleset rather than hand-editing the one named Redline — a hand edit is drift, and redline verify will report it as exactly that.

2. Stand a Redline job down and run your own instead

Where you already run something that covers a gate job, tell the gate so rather than running both. redline init --integrations records the tools you have, and writes stand-down: into the caller from what you declared — never from detection alone. A stood-down job is skipped and the aggregate reads a skip as a pass.

policy, dependencies and secrets are the three that can be stood down. The last two are the ones no label can waive, so standing one of those down is the one edit here that removes a security check — do it because another scanner genuinely covers it, and never to get a red pull request green.

3. Add a job to the gate itself

For a check the whole organisation should get. Two rules, and the second is the one people miss:

  • Do not rename any job id. The required check name is built from the caller job id and the called job id — redline-gate and gate. Renaming either changes the required context, and every pull request in every onboarded repository sits on “Expected — waiting for status” forever.
  • Add the new job to the aggregate's needs: list. A job that is not in needs still runs and still shows up red on the pull request, but the gate job never waits for it and never reads its result — so it blocks nothing. A check that looks like it is enforcing and is not is worse than no check.

A new job is a process check: it sits on the ladder and a recorded exemption can waive it. The never-exemptible set is exactly dependency review and the secret scan, and it is deliberately not a list you extend from a repository — a security floor that each repository defines for itself is not a floor.

Where you edit depends on where your gate lives. With --gate-source org, the job definitions are in <org>/.github — one merge there reaches every onboarded repository, which is the point of hosting it once.

With --gate-source local the gate is vendored into your repository, and its header says “Regenerate with redline init --repair; edits here are overwritten.” That is not a warning about style — the next --repair, which is also how you pick up a gate fix or clear a pending admin capability, will silently delete your job. Put the check in its own workflow (option 1), or contribute it to the Redline source repository so every repository gets it and yours stops being a fork.

Whichever route you take, prove it did what you think: Verification scenario 8 shows a stood-down job reading as a pass, and scenarios 5 and 9 show the difference between a check that blocks and one that only reports.

Policy invariants

  • One human approval, always — automated review is advisory input, never the approver.
  • Review threads must be resolved before merge.
  • Applied per repo by redline init, as a GitHub ruleset named Redline or an Azure DevOps branch policy — no manual per-repo setup, no drift from a hand-edited setting.
  • Advisory by default in Phase 1 on both hosts. Promotion to blocking is a deliberate second step, after a soak period, not an onboarding default.