How it works
Mechanism
Conveyor treats merging the way a database treats transactions: optimistically, with validation at commit time and cheap retries. The AI context adds the thing databases never had — retries are nearly free. Re-running an agent against a new head costs cents and minutes; asking a human to rebase costs hours. Every mechanism below is built on that asymmetry.
The life of an agent PR
- Claim. Before work starts, the agent or scheduler reserves a write-set:
conveyor claim "packages/api/src/billing/**" --task BILL-1234. Overlapping claims are refused (or warned, per config) — the collision is resolved before any code exists. - Work. The agent branches from main, does the task, and writes
.conveyor/intents/<task>.md— a self-contained restatement of the task. The manifest is the durable artifact; the diff is a cache of it. - Enqueue. PR opened,
conveyor:queuedlabel applied. FIFO order, with aconveyor:urgentlane that jumps ahead. - Speculate. The coordinator assigns a train position and builds the speculative branch. Fast checks run on every position in parallel.
- Land. Green positions merge in order via fast-forward — the tested SHA is byte-for-byte the landed SHA. Claims release automatically.
- Verify. The full suite runs post-merge. Failures bisect to a culprit and dispatch repair.
Speculative trains, in detail
A train is a chain of branches, each testing the predicted future state of main:
main ──● (current head)
\
●──────── conveyor/train/1 = main + PR#101
\
●─────── conveyor/train/2 = main + PR#101 + PR#104
\
●────── conveyor/train/3 = main + PR#101 + PR#104 + PR#107
All positions run concurrently. With a 10-minute fast check and batch_size: 8, steady-state throughput reaches 48 PRs/hour on one repo — a serialized queue with the identical check manages 6. Because positions are cumulative, failure attribution is built in: position 2 green and position 3 red means PR#107 is the culprit. No pre-merge bisection is ever needed.
When a position fails
- The culprit PR is evicted — labeled, claim retained, check log attached as a PR comment.
- Positions behind it are rebuilt without it, reusing green prefixes: one eviction costs one round of checks for the tail, not a restart.
- If the PR's manifest allows it, Conveyor dispatches regeneration. Human PRs get a comment instead.
- Flake shield: with
train.retries: 1, a red position re-runs once before eviction.
When a merge conflict occurs
A PR that no longer merges cleanly onto its position isn't an error — it's cache invalidation. It's evicted immediately (no CI spent) and routed to regeneration.
Regeneration: replay the intent, don't rebase the diff
A regeneration job checks out fresh main on your runners (same-repo branches only, never forks), runs your configured agent.regenerate command with .conveyor/intents/<task>.md as the task, force-pushes the PR branch, and re-enqueues. The agent re-derives the change in current context instead of patching stale text.
Regeneration counts live in refs/conveyor/state. Past max_regenerations (default 3) the PR parks as conveyor:failed and a human is mentioned. A repeatedly regenerating intent is a signal: it usually means two tasks are genuinely coupled and should be one.
The post-merge gate
Pre-merge checks are a sampling strategy, not a proof. full_check.run executes against main after every landing, debounced to the latest head under load. On failure:
- Bisect. If the last green main was several landings back, Conveyor bisects the merge commits in between to isolate the culprit.
- Repair per
full_check.on_failure:fix-forward(default) dispatches an agent job with the failure output plus the culprit's intent — the fix PR enters the urgent lane; aftermax_fix_attemptsit falls back torevert.notifydoes neither, just pages. - Contain. Landing continues while main is red by default; set
full_check.block_on_red: trueto pause the train instead.
Runtime risk beyond tests belongs to feature flags and staged rollout, not to a longer pre-merge queue. Conveyor's contract ends when main is green — it is not your deploy system.
State model — no server, no database
| Location | Contents |
|---|---|
refs/conveyor/state | Queue order, train assignments, regeneration counts, landing + bypass history (JSON, compare-and-swap updates; concurrent ticks are safe) |
refs/conveyor/claims | Active write-set claims: task id, globs, owner, expiry |
conveyor/train/* | Speculative merge branches under test; deleted after landing or rebuild |
| PR labels | Human-visible status (conveyor:queued … conveyor:failed) |
The coordinator (conveyor run) is a stateless tick: read refs + GitHub API, compute the delta, apply, exit. It fires on webhook events plus a cron safety net.
What Conveyor deliberately is not
- Not a CI system — your workflows run the checks; Conveyor decides what to test and what to merge.
- Not a build cache — making the fast check fast is your job (Turborepo, Nx, Bazel, path filters).
- Not an agent framework —
agent.*commands are shell templates; any CLI agent works. - Not a deploy pipeline.
Team setup
~30 min to a working train · +30 for agent machinery
Steps 1–6 give you a speculative merge queue for all PRs, human and agent. Steps 7–8 add the agent-specific machinery. Prerequisites: repo admin, Actions enabled, Node 20+, and a test command that finishes in minutes when scoped to a change.
Install and initialize
npm install -g conveyor-ci
cd your-repo
conveyor init
init writes conveyor.yml plus four workflows — coordinator (webhooks + 5-min cron), fast-check (on push to conveyor/train/**), post-merge (full check + bisect), and agent (regeneration/fix-forward, dispatch-only) — and creates the conveyor:* labels.
Configure the two checks
version: 1
branch: main
fast_check:
run: pnpm turbo run test lint --filter="...[origin/main]"
full_check:
run: pnpm test && pnpm playwright test
The fast check must be deterministic and change-scoped — affected-graph filters are the easiest route; target under 10 minutes. The full check is everything you run pre-merge today, moved post-merge wholesale. Don't trim it — continuous execution is the point. No graph tooling? Use fast_check.test_map to route by touched paths; crude beats slow.
Grant the coordinator write access
The coordinator creates branches, fast-forwards main, edits labels, comments. The built-in GITHUB_TOKEN covers building trains, running checks, and evicting — but not landing: GitHub refuses to let the Actions app's own token fast-forward a branch through required status checks, even when the check is green on the exact SHA (found dogfooding this repo). Store a GitHub App token or fine-grained PAT as CONVEYOR_TOKEN (contents + pull-requests write, actions write, this repo only) — it's required for landing, keeps merges auditable, and lets branch protection exempt exactly one actor.
To mint the PAT (needs a human — GitHub demands sudo-mode passkey/2FA re-auth first): github.com/settings/personal-access-tokens/new → name it, resource owner = the org (if the org isn't listed it disallows fine-grained PATs; fall back to a classic PAT with repo + workflow scopes), repository access = only this repo, permissions Contents / Pull requests / Actions all read-write → generate (orgs may hold it for approval) → gh secret set CONVEYOR_TOKEN --repo <owner>/<repo> and paste at the prompt. No "Checks" permission exists for fine-grained PATs — Actions read/write is what makes check state readable (GitHub maps reads of Actions-created check runs to the Actions permission; a token without it 403s every tick on /check-runs). Conveyor also falls back to the Actions workflow-runs API if the Checks API is denied, which requires the fast-check workflow's name: to equal train.check (the scaffolding already does).
Protect main
- Require the
fast-checkstatus (or whatevertrain.checknames) — the landed SHA is the tested train commit, so it already carries that check; no separate "conveyor" check exists. - Block direct pushes for everyone except the Conveyor identity.
- Turn off GitHub's own merge queue and auto-merge — two coordinators fighting over order helps no one.
Humans stop pressing the merge button entirely; enqueueing is the only path in.
Commit, push, verify the loop
git add conveyor.yml .github/workflows
git commit -m "Add Conveyor merge coordination"
git push
conveyor status
status should print an empty queue and a healthy coordinator. Permission errors here mean step 3 isn't done.
Land your first train
conveyor merge 101 102 103 # or add the conveyor:queued label in the UI
Watch conveyor status --watch: train branches appear, fast checks run in parallel, and all three PRs land in one fast-forward. Milestone: throughput is now batch_size per fast-check cycle. Everything past here is agent-specific.
Wire up the agent runner
Add your agent's API key to Actions secrets (e.g. ANTHROPIC_API_KEY) and configure the commands:
agent:
regenerate: >
claude -p "$(cat "$CONVEYOR_INTENT")"
--permission-mode acceptEdits --max-turns 80
fix_forward: >
claude -p "$(cat .conveyor/fix-task.md)"
--permission-mode acceptEdits --max-turns 80
full_check:
on_failure: fix-forward
max_fix_attempts: 2
For fix-forward jobs the coordinator materializes .conveyor/fix-task.md (culprit's intent + failure log excerpt) before invoking the command.
Agent jobs run only for same-repo branches; fork PRs are never regenerated and never see secrets. The agent job's token writes only the one PR branch it's repairing. Secrets are exposed only to the agent workflow, never to check workflows.
Turn on claims and intent enforcement
claims:
enforce: warn # week 1: warn → then: block
ttl_hours: 24
enqueue:
require_intent: false # flip true once agent PRs all carry manifests
require_approval: false
Then give your agents the protocol — drop the agent snippet into AGENTS.md / CLAUDE.md, and put conveyor claim into your task dispatcher if you have one.
Rollout advice
- Week 1: train only, claims off. Confirm throughput; watch for evictions that go green on re-run — that's flaky tests, and the train amplifies flake pain (one flake evicts a real PR and rebuilds the tail).
- Week 2: intent manifests on agent PRs, regeneration on, claims
warn. - Week 3: claims
block,require_intent: true, fix-forward on. Full design capacity.
conveyor.yml
Complete schema · defaults shown · only branch, fast_check.run, full_check.run required
version: 1
branch: main # protected target branch
train:
batch_size: 8 # max concurrent speculative positions
check: fast-check # GitHub check name required green on train branches
timeout_minutes: 20 # evict a position whose check hangs past this
retries: 1 # re-run a red position once before evicting
fast_check:
run: <command> # REQUIRED. Change-scoped; target < 10 min
test_map: # optional path-routed commands, matched in order;
"packages/api/**": <cmd> # all commands whose globs match touched files run
"**": <cmd> # fallback
full_check:
run: <command> # REQUIRED. Full suite; runs post-merge on `branch`
on_failure: fix-forward # fix-forward | revert | notify
max_fix_attempts: 2 # failed fixes before falling back to revert
block_on_red: false # true = pause landings while main is red
debounce_minutes: 5 # under load, test only the latest head this often
agent:
regenerate: <command> # run in fresh checkout: new main + intent.md
fix_forward: <command> # run on fresh branch off main; fix-task.md materialized
max_regenerations: 3 # per-PR; exceeded → conveyor:failed + human mention
claims:
enforce: warn # off | warn | block
ttl_hours: 24 # claims expire; regeneration renews automatically
enqueue:
label: conveyor:queued # applying this label enqueues a PR
require_approval: false # true = ≥1 human approval gates enqueueing
require_intent: false # true = reject agent PRs without intent.md
urgent_label: conveyor:urgent # jumps FIFO (used by fix-forward/revert PRs)
Intent manifest
.conveyor/intents/<task>.md · present in every agent-authored PR
YAML frontmatter plus a self-contained task body. The test of a good manifest: an agent with no other context, on a fresh checkout of a future main, can redo the work from this file alone.
---
task: BILL-1234 # stable task id (ticket, or any unique string)
agent: claude-code # informational: which runner authored this
write_set: # globs this task is allowed to touch
- packages/api/src/billing/**
- packages/api/test/billing/**
acceptance: # verifiable completion criteria
- "pnpm --filter api test billing passes"
- "upgrade preview shows prorated amount"
regenerate: allow # allow | never (never = evictions go to a human)
---
# Task
Add proration support to subscription upgrades.
When a customer upgrades mid-cycle, charge the difference between
plans, prorated by remaining days. Use the existing InvoiceCalculator;
do not introduce a new billing path. Edge cases: annual→monthly is not
proration, it is a plan change at renewal — reject with UPGRADE_DEFERRED.
write_setis validated against the actual diff at enqueue — files changed outside it failconveyor intent check(warn or block perclaims.enforce). The write-set is checked, not trusted.- The body is passed verbatim to
agent.regenerate— write it as a prompt, not documentation. Constraints and named edge cases are exactly what a regenerated attempt would otherwise lose. - Regeneration counts live in
refs/conveyor/state, keeping the manifest byte-stable across regenerations.
CLI · labels · refs
Reference
| Command | What it does |
|---|---|
conveyor init | Scaffold config, workflows, and labels in the current repo |
conveyor status [--watch] [--json] | Queue, train positions, check states, claims, last coordinator tick |
conveyor merge <pr...> | Enqueue PRs (equivalent to applying the queue label) |
conveyor claim <glob...> --task <id> [--ttl 24h] | Reserve a write-set; fails on overlap when enforce: block |
conveyor release --task <id> | Release a claim early (merge releases automatically) |
conveyor claims [--json] | List active claims with owners and expiry |
conveyor intent new --task <id> | Scaffold .conveyor/intents/<task>.md |
conveyor intent check | Validate manifest schema, write-set vs. actual diff, overlap vs. claims |
conveyor bypass <pr> --reason "..." | Direct merge outside the train; audited in state history |
conveyor pause / resume | Stop/restart new trains and landings; running checks finish and report |
conveyor run | One coordinator tick (invoked by Actions; safe to run manually) |
| Label | Meaning | Applied by |
|---|---|---|
conveyor:queued | Waiting for a train position | you / agent |
conveyor:urgent | Jumps the FIFO order | you / fix-forward / revert |
conveyor:in-train | Occupying a position; checks running | coordinator |
conveyor:evicted | Failed its position or hit a conflict; log attached | coordinator |
conveyor:regenerating | Agent re-deriving the change against fresh main | coordinator |
conveyor:failed | Out of regeneration attempts; needs a human | coordinator |
Everything is inspectable with plain git:
git fetch origin 'refs/conveyor/*:refs/conveyor/*'
git cat-file -p refs/conveyor/state
Branch protection requires the train's own check (train.check, default fast-check) — Conveyor posts no separate check. Landing is a fast-forward to the tested merge commit, so tested SHA ≡ landed SHA and the landed commit already carries the green check: protection and the train share one source of truth. A landed commit means this exact SHA passed the fast check at its train position. Want linear history? Keep PRs single-commit at authoring time. Landing itself requires CONVEYOR_TOKEN — the Actions app's own GITHUB_TOKEN cannot fast-forward through required checks.
Agent guide
Protocol · drop-in snippet · Claude Code example
Conveyor is agent-agnostic: anything that can run in a shell, edit files, and push a branch can follow the protocol.
The five rules
- Claim before you code. If the claim is refused, the files are someone else's right now — pick another task. Never start work on files you couldn't claim.
- Write the intent first, as a complete, self-contained prompt. Update it if the task's shape changes while you work.
- Stay inside your write-set. Need more files? Widen the claim and the manifest — don't silently touch unclaimed paths.
- Enqueue, then walk away. Don't watch the queue, don't rebase, don't resolve conflicts. Evictions regenerate from your manifest against fresh main; that attempt supersedes yours.
- A fix-forward task is a normal task. Invoked with
.conveyor/fix-task.md? Fix the failure, keep the original acceptance criteria true, enqueue as usual.
Drop-in snippet for AGENTS.md / CLAUDE.md
## Merging (Conveyor)
This repo merges through Conveyor, not the GitHub merge button.
- Before starting a task, reserve the files you'll touch:
`conveyor claim "<glob>" --task <task-id>`. If refused, do not
start — the files are claimed by another task.
- Create .conveyor/intents/<task>.md with `conveyor intent new --task <id>`.
Write the body as a full self-contained prompt for redoing this
task from scratch: goal, constraints, named edge cases, acceptance
criteria. Keep the write_set accurate.
- Before opening the PR: run `conveyor intent check` and make sure
the fast check passes locally (conveyor.yml → fast_check.run).
- Open the PR and add the conveyor:queued label. You are done.
- NEVER: rebase onto main, resolve merge conflicts, force-push after
enqueueing, merge directly, or re-enqueue an evicted PR without new
changes. Evictions and conflicts are handled by regeneration.
- If your PR gets conveyor:failed, a human will look at it; move on.
Worked example: dispatching with Claude Code
TASK=BILL-1234
conveyor claim "packages/api/src/billing/**" \
"packages/api/test/billing/**" \
--task $TASK || exit 1 # refused claim = don't dispatch
claude -p "$(cat tasks/$TASK.md)
Follow the Merging (Conveyor) section of AGENTS.md: write
.conveyor/intents/<task>.md for task $TASK, keep changes inside the claimed
write-set, run 'conveyor intent check' and the fast check before
opening the PR, then open the PR and label it conveyor:queued." \
--permission-mode acceptEdits
Scheduler integration
If a coordinator process assigns tasks to a fleet, put the claim step in the scheduler — refusing a dispatch is cheaper than refusing a PR:
for task in ready_tasks:
if conveyor claim <task.write_set> --task <task.id>:
dispatch(task) # agent inherits an already-held claim
else:
defer(task) # overlapping work in flight; retry later
Predicting a write-set before code exists is approximate — that's fine. Claims are advisory collision-avoidance; the enforced boundary is diff-vs-manifest validation at enqueue. Start with directory-level globs and tighten. Tasks whose write-sets always collide are one task wearing two ticket numbers — merge them.
Operations
Daily watch · situations · tuning · troubleshooting
Watching the system
$ conveyor status
queue 4 waiting · 6 in train · throughput 31 PRs/h (24h avg)
train 1 #211 ✓ 2 #214 ✓ 3 #209 ●running 4 #216 ●running
5 #217 ○queued-check 6 #219 ○queued-check
main green · full_check 14m ago · last landing 6m ago
claims 7 active · oldest 19h (BILL-1204)
attention #198 conveyor:failed (3 regenerations) · #201 evicted, human PR
Daily glances: the attention rows (the only things needing a human); the eviction rate (healthy ≈ zero; evictions that go green on re-run are flaky tests — fix or quarantine fast, because one flake evicts a real PR and rebuilds the tail); regeneration counts (the same intent regenerating repeatedly means coupled tasks — merge them, don't raise the limit); and claim age (claims older than a work session are usually dead agents; they expire at ttl_hours).
Common situations
- PR stuck at
conveyor:failed: read the attached logs; fix as a human PR, or sharpen the intent and re-enqueue (which resets the counter), or close it. - Main red, fix-forward looping: after
max_fix_attemptsConveyor auto-reverts. If the revert itself won't land, that's the one case forconveyor bypass. - Emergency merge:
conveyor bypass <pr> --reason "..."— lands directly, audited, train rebuilds on top. Bypassed commits skip the fast gate; the post-merge gate still covers them. Bypassing weekly means your fast check tests the wrong things. - Humans: they enqueue like everyone else — the train is strictly faster than a full pre-merge suite. No manifest means evictions come back as a comment, not a regeneration.
Tuning
| Symptom | Adjustment |
|---|---|
| Queue grows faster than it drains | Make fast_check faster first (it dominates); then raise batch_size |
| Evictions cascade into rebuilds | Lower batch_size — deep speculation only pays when positions usually pass; fix the failure rate |
| Flaky fast check | retries: 1 is a bandage — quarantine flakes into full_check |
| Full check red for long stretches | Lower debounce_minutes; verify fix-forward dispatches; consider block_on_red while stabilizing |
| Agents collide despite claims | Coarsen globs to directory level; move claiming into the dispatcher |
| Runner queue saturated | Budget batch_size × jobs concurrent runners, or lower batch_size |
Troubleshooting
- Trains build but nothing lands — almost always permissions. If the tick log shows
Required status check "fast-check" is expectedon thePATCH …/refs/heads/maincall even though the check is green on that exact SHA, the coordinator is running on the defaultGITHUB_TOKEN, which GitHub bars from fast-forwarding through required checks — setCONVEYOR_TOKEN. Otherwise check the token identity is exempt from push restrictions and the workflow hascontents: write. - Fast checks never start on train branches — the coordinator triggers them by explicit
workflow_dispatchper position (pushes from the defaultGITHUB_TOKENdon't fire push workflows — GitHub's recursion guard). If checks never start, the fast-check workflow is missing itsworkflow_dispatch:trigger or the coordinator lacksactions: write. Thepush: branches: ['conveyor/train/**']trigger is belt-and-braces for PAT/App-token coordinators. - Regeneration jobs fail immediately — check the agent secret is set for the agent workflow, and that the branch is same-repo (forks are excluded by design).
- State looks wrong — the refs are plain git objects; inspect them directly. Nuclear option: delete both refs; the coordinator rebuilds from labels next tick (regeneration counts reset).
- CAS conflict between two ticks — expected and harmless; the loser retries.
Objections
Every team raises these · answered honestly
Several of these shaped the design. Two are real costs accepted deliberately; the rest are either tunables or costs teams already pay in a form today's tooling hides.
- "You're merging code that hasn't passed the full suite."
- The pre-merge full suite was already an illusion — it tested against a main that had moved by merge time, and said nothing about the PRs landing alongside yours. Conveyor makes the sampling explicit: the fast gate samples pre-merge, the full suite runs more often than before (continuously), and every landing is attributable. Risk postures that can't tolerate a red-main window get
block_on_red: trueandrequire_approval: true— less throughput, same machinery. - "A bot with write access to main is a security hole."
- It's one scoped identity with one audit trail, replacing merge rights held by every developer and agent. Agent secrets live in a single dispatch-only workflow that never runs for forks; bypasses are recorded in state history. This is attack-surface reduction.
- "Force-push on regeneration breaks code review."
- A real cost, accepted deliberately. The unit of review moves to the intent — task, constraints, acceptance — plus the final diff, not the churn between attempts. Gate with
require_approval: true(reviews the last diff that actually merges), or opt out per-PR withregenerate: never. Human branches are never force-pushed. - "Our suite takes hours; we can't make a 10-minute fast check."
- You need a fast subset, not a fast suite: build + lint + unit tests of touched packages via
test_map, no build-system migration. Crude beats slow — a 7-minute check catching 85% of failures pre-merge beats a 2-hour check catching 98%, because the tail is caught minutes later by the continuous full suite and repaired automatically. - "N speculative runs — CI cost explodes."
- Per-position runs are roughly the same count as testing each PR once (which you do today), just against the predicted future state, in parallel. Conflicts cost zero CI (caught at merge, before checks). Weigh the delta against engineer-hours spent rebasing and babysitting a stale queue — for agent fleets, it isn't close.
- "Regeneration will loop and burn tokens."
max_regenerations(default 3) parks thrashers for a human. The loop is also a signal: an intent that keeps regenerating is two coupled tasks wearing different ticket numbers.- "Developers won't write intent manifests."
- They never do — manifests are written by agents, as part of the agent's task. Human PRs ride the train with no manifest and still merge faster. Pillars 1–3 are a pure win for all-human teams.
- "Claims will deadlock us."
- Claims are advisory (
off/warn/block) with a TTL so dead agents can't squat, and only need directory-level accuracy — the enforced boundary is diff-vs-manifest validation, which checks what actually happened. Start atwarn: the refusals it logs are collisions you were paying for as merge conflicts. - "Flaky tests will make the train thrash."
- Yes — the train amplifies flake pain, and
retriesis deliberately only a bandage. Flakes were always costing you; the train makes the cost visible and attributable within days, with eviction logs naming the test. Quarantine them intofull_check. - "Merge commits pollute our linear history."
- The other deliberate cost. Fast-forwarding to the tested merge commit is what makes green trustworthy — squashing would land a SHA that was never tested. Single-commit PRs keep history effectively linear.
- "We already have GitHub's merge queue / Mergify / Graphite."
- Those are pillar 2 for human-rate traffic. Conveyor adds the gate split with automated repair and the agent-native machinery. If all PRs are human and the suite is fast, GitHub's queue may genuinely be enough.
- "What happens when Conveyor itself breaks?"
- The failure mode is boring by construction: no server, so worst case nothing merges — never "wrong things merge", because landing requires a green check on the exact SHA. Escape hatch: disable the coordinator workflow and merge by hand. State rebuilds from labels.
FAQ
Design decisions, answered
- How is this different from GitHub's merge queue?
- GitHub's queue gives you speculation. Conveyor adds the other three pillars: the fast/full gate split with post-merge culprit-finding and automated repair, plus the agent-specific machinery — intent manifests, regeneration-instead-of-rebase, and write-set claims. All-human team? GitHub's queue may be enough.
- Does it work without any AI agents?
- Yes — the train, gate split, and auto-revert are agent-free. You just won't use manifests, claims, or regeneration.
- Why no squash merges?
- Landing is a fast-forward to the tested merge commit, so tested SHA ≡ landed SHA — that identity is the correctness core of the train. Want tidy history? Author single-commit branches.
- Monorepo or many repos?
- One instance per repo, and the model favors monorepos — claims and trains coordinate within a repo. Two repos changing in lockstep constantly is a repo-boundary problem Conveyor can't fix.
- What stops an agent from ignoring the protocol?
- Branch protection stops direct merges for everyone.
require_intentandclaims.enforce: blockreject non-conforming PRs at enqueue, and the write-set is validated against the actual diff — checked, not trusted. - What about PRs that landed after a culprit, while main was red?
- They landed on a fast-check green and stay merged; repair enters the urgent lane rather than rolling anyone back. If that window is unacceptable for your risk posture, set
block_on_red: trueand take the throughput cost while red. - GitLab / Bitbucket?
- Not yet. The design needs only branches, refs, checks, and a labels-equivalent — ports are feasible; GitHub is first.