Ворота слияния
Вы узнаете
Когда мерджер может слить пулл-реквест, который одобрила пчела-ревьюер.
Пчела-ревьюер одобряет, мерджер сливает; merger_gate.t27 — правило между ними. check_passes пропускает зелёную проверку и задерживает любую, которая ещё идёт. Красная проходит, только если она не обязательная, завершилась и пчела назвала её списанной, а если набор правил не читается, обязательной считается каждая проверка. gate_open нужен хотя бы 1 опубликованный результат, 0 идущих и 0 блокирующих: ноль проверок — не ноль падений.
Попробовать
Дайте идущей проверке завершиться зелёной; затем сделайте набор правил нечитаемым и посмотрите на списанную красную.

Four checks on a pull request the reviewer bee approved. Tap what each one is: check_passes judges it, and gate_open opens only when one has posted, none runs and none blocks.
specs/queen/merger_gate.t27
// SPDX-License-Identifier: Apache-2.0
; specs/queen/merger_gate.t27 -- when the merger may merge a pull request the reviewer bee approved
; The merger is .github/workflows/auto-merge-ready-prs.yml, step "Find ready PRs" (bash and jq).
; It does not import this spec, and t27c gen-js lowers no function bodies (#7300).
; The two meet in tools/bees/merger_gate_selftest.py: a test below that names a scenario
; asserts the verdict that scenario pins for the workflow.
; Negative controls: discounted required check, unconcluded check, required check never posted,
; unreadable ruleset with a discounted red, zero posted checks, a red discounted while master's run
; of the same check was CANCELLED (#7317)
; ASCII only (L3)
; phi^2 + 1/phi^2 = 3 | TRINITY
module QueenMergerGate;
/// With the ruleset unreadable the merger cannot tell which checks are required,
/// so every check counts as required and no red one is discountable (#5776 item 2).
pub fn counts_as_required(ruleset_readable: bool, required: bool) -> bool {
return !ruleset_readable || required;
}
/// A red check passes only when it is not required, has concluded, and is discounted
pub fn red_check_passes(required: bool, concluded: bool, discounted: bool) -> bool {
return !required && concluded && discounted;
}
/// A required check passes when it is posted and green
pub fn required_check_passes(posted: bool, green: bool) -> bool {
return posted && green;
}
/// One posted check. Not concluded (PENDING, EXPECTED, no state yet) blocks;
/// green (SUCCESS or SKIPPED) passes; a red passes only as a discounted check
/// that does not count as required.
pub fn check_passes(ruleset_readable: bool, required: bool, concluded: bool, green: bool, discounted: bool) -> bool {
if (!concluded) { return false; }
if (green) { return true; }
return red_check_passes(counts_as_required(ruleset_readable, required), true, discounted);
}
// ---------------------------------------------------------------------------
// What master says about a discounted red (#7317)
//
// The bee's `discounted-check:` line says "red on master too". On 2026-10-06 PR #6956 merged with its
// Corpus ratchet red because master's run of that check at the PR's base was CANCELLED, and a cancelled
// run was read as a red one. A cancelled run says nothing about the code: a superseding push stopped it.
// So the line is necessary and no longer sufficient. The merger reads master itself: the runs of the
// red check's own job on master, newest first, and only a COMPLETED FAILURE at the PR's merge base or
// later makes the red inherited. Everything else is UNKNOWN, and UNKNOWN never discounts.
/// One master run of the red check, as the merger reads its job's status and conclusion.
/// The codes are mirrored by number in the merger's selftest fixtures; a new one takes the next code.
pub const RUN_MISSING : u8 = 0;
pub const RUN_FAILURE : u8 = 1;
pub const RUN_SUCCESS : u8 = 2;
pub const RUN_CANCELLED : u8 = 3;
pub const RUN_SKIPPED : u8 = 4;
pub const RUN_TIMED_OUT : u8 = 5;
/// queued or in progress
pub const RUN_RUNNING : u8 = 6;
/// neutral, action_required, startup_failure, stale: completed, but neither a pass nor a fail
pub const RUN_OTHER : u8 = 7;
/// What master says about the PR's red.
pub const MASTER_UNKNOWN : u8 = 0;
/// a completed failure: the red is inherited
pub const MASTER_RED : u8 = 1;
/// a completed success: master was green, the red is the PR's own
pub const MASTER_GREEN : u8 = 2;
/// One run on its own. Only a completed failure or a completed success decides anything.
pub fn run_says(run: u8) -> u8 {
if (run == RUN_FAILURE) { return MASTER_RED; }
if (run == RUN_SUCCESS) { return MASTER_GREEN; }
return MASTER_UNKNOWN;
}
/// One step of the look-back over master's runs, newest first, starting from MASTER_UNKNOWN.
/// The newest DECISIVE run wins: a cancelled newest run is looked past, a success is not.
/// A run older than the PR's merge base is not evidence about the code the PR started from.
pub fn look_back(so_far: u8, at_or_after_merge_base: bool, run: u8) -> u8 {
if (so_far != MASTER_UNKNOWN) { return so_far; }
if (!at_or_after_merge_base) { return MASTER_UNKNOWN; }
return run_says(run);
}
/// A red is discounted only when the bee's approval names it AND master says red.
/// This is the `discounted` that check_passes takes.
pub fn discount_holds(bee_named: bool, master: u8) -> bool {
return bee_named && master == MASTER_RED;
}
/// The gate opens when at least one check has posted, none is still running,
/// and none blocks. Zero checks is not zero failures.
pub fn gate_open(posted: u32, running: u32, blocking: u32) -> bool {
return posted > 0 && running == 0 && blocking == 0;
}
// Tests for counts_as_required
test counts_as_required_follows_the_ruleset_when_readable {
assert counts_as_required(true, true) == true;
assert counts_as_required(true, false) == false;
}
test counts_as_required_is_every_check_when_unreadable {
assert counts_as_required(false, false) == true;
assert counts_as_required(false, true) == true;
}
// Tests for red_check_passes
// Positive case: non-required check that concluded and is discounted
test red_check_passes_valid_discounted_non_required {
assert red_check_passes(false, true, true) == true;
}
// Negative control: discounted required check (required=true, cannot be discounted)
test red_check_passes_discounted_required_fails {
assert red_check_passes(true, true, true) == false;
}
// Negative control: unconcluded check (concluded=false, even if discounted)
test red_check_passes_unconcluded_fails {
assert red_check_passes(false, false, true) == false;
}
// Negative control: non-required check that concluded but not discounted
test red_check_passes_non_discounted_fails {
assert red_check_passes(false, true, false) == false;
}
test red_check_passes_all_false_fails {
assert red_check_passes(true, false, false) == false;
}
// Tests for required_check_passes
test required_check_passes_posted_and_green_passes {
assert required_check_passes(true, true) == true;
}
// Self-test scenario "required check never posted"
test required_check_passes_not_posted_fails {
assert required_check_passes(false, true) == false;
}
test required_check_passes_posted_but_not_green_fails {
assert required_check_passes(true, false) == false;
}
test required_check_passes_neither_posted_nor_green_fails {
assert required_check_passes(false, false) == false;
}
// Tests for check_passes: (ruleset_readable, required, concluded, green, discounted)
test check_passes_green_advisory_and_green_required_pass {
assert check_passes(true, false, true, true, false) == true;
assert check_passes(true, true, true, true, false) == true;
}
// Self-test scenario "red advisory check, discounted in the bee's approval"
test check_passes_red_advisory_discounted_passes {
assert check_passes(true, false, true, false, true) == true;
}
// Self-test scenario "red advisory check, not discounted"
test check_passes_red_advisory_not_discounted_blocks {
assert check_passes(true, false, true, false, false) == false;
}
// Self-test scenario "red REQUIRED check, discounted anyway"
test check_passes_red_required_discounted_blocks {
assert check_passes(true, true, true, false, true) == false;
}
// Self-test scenarios "a check still running" and "pending commit status blocks":
// not concluded blocks whatever else is true, a discount included
test check_passes_running_check_blocks {
assert check_passes(true, false, false, true, true) == false;
assert check_passes(true, false, false, false, true) == false;
}
// Self-test scenario "ruleset unreadable: a discounted red check still blocks"
test check_passes_unreadable_ruleset_discounted_red_blocks {
assert check_passes(false, false, true, false, true) == false;
}
// Self-test scenario "ruleset unreadable, every check green"
test check_passes_unreadable_ruleset_green_passes {
assert check_passes(false, false, true, true, false) == true;
}
// Tests for gate_open: (posted, running, blocking)
// Self-test scenario "bee approved this head after it arrived, bee labeled": two green checks
test gate_open_posted_checks_all_pass_opens {
assert gate_open(2, 0, 0) == true;
assert gate_open(1, 0, 0) == true;
}
// Negative control: zero checks is not zero failures
test gate_open_zero_checks_stays_closed {
assert gate_open(0, 0, 0) == false;
}
// Self-test scenario "a check still running"
test gate_open_running_check_keeps_it_closed {
assert gate_open(3, 1, 0) == false;
}
// Self-test scenario "two red checks, only one discounted"
test gate_open_one_blocking_check_keeps_it_closed {
assert gate_open(4, 0, 1) == false;
}
// Tests for run_says, look_back and discount_holds (#7317)
test only_a_completed_failure_says_red {
assert run_says(RUN_FAILURE) == MASTER_RED;
assert run_says(RUN_SUCCESS) == MASTER_GREEN;
}
test every_other_run_says_unknown {
assert run_says(RUN_MISSING) == MASTER_UNKNOWN;
assert run_says(RUN_CANCELLED) == MASTER_UNKNOWN;
assert run_says(RUN_SKIPPED) == MASTER_UNKNOWN;
assert run_says(RUN_TIMED_OUT) == MASTER_UNKNOWN;
assert run_says(RUN_RUNNING) == MASTER_UNKNOWN;
assert run_says(RUN_OTHER) == MASTER_UNKNOWN;
}
// Self-test scenario "master newest cancelled, older failure at or after the merge base"
test look_back_passes_a_cancelled_run_to_an_older_failure {
assert look_back(look_back(MASTER_UNKNOWN, true, RUN_CANCELLED), true, RUN_FAILURE) == MASTER_RED;
}
// Self-test scenario "master failure only before the merge base"
test look_back_stops_at_the_merge_base {
assert look_back(look_back(MASTER_UNKNOWN, true, RUN_CANCELLED), false, RUN_FAILURE) == MASTER_UNKNOWN;
assert look_back(MASTER_UNKNOWN, false, RUN_SUCCESS) == MASTER_UNKNOWN;
}
// The newest decisive run wins, in both directions
test look_back_keeps_the_newest_decisive_run {
assert look_back(look_back(MASTER_UNKNOWN, true, RUN_SUCCESS), true, RUN_FAILURE) == MASTER_GREEN;
assert look_back(look_back(MASTER_UNKNOWN, true, RUN_FAILURE), true, RUN_SUCCESS) == MASTER_RED;
assert look_back(MASTER_RED, false, RUN_SUCCESS) == MASTER_RED;
}
test look_back_over_only_unknown_runs_stays_unknown {
assert look_back(look_back(look_back(MASTER_UNKNOWN, true, RUN_CANCELLED), true, RUN_RUNNING), true, RUN_SKIPPED) == MASTER_UNKNOWN;
}
// Self-test scenario "master failure": discounted
test discount_holds_on_a_master_failure {
assert discount_holds(true, look_back(MASTER_UNKNOWN, true, RUN_FAILURE)) == true;
assert check_passes(true, false, true, false, discount_holds(true, MASTER_RED)) == true;
}
// NEGATIVE CONTROL, the #6956 incident: PR red while master's run is CANCELLED -> blocked
test pr_red_while_master_cancelled_is_blocked {
assert discount_holds(true, look_back(MASTER_UNKNOWN, true, RUN_CANCELLED)) == false;
assert check_passes(true, false, true, false, discount_holds(true, look_back(MASTER_UNKNOWN, true, RUN_CANCELLED))) == false;
}
// Self-test scenario "master success": the red is the PR's own -> blocked
test pr_red_while_master_green_is_blocked {
assert discount_holds(true, MASTER_GREEN) == false;
assert check_passes(true, false, true, false, discount_holds(true, MASTER_GREEN)) == false;
}
// Master red, but the bee never named the check: blocked
test master_red_without_the_bees_line_is_blocked {
assert discount_holds(false, MASTER_RED) == false;
assert discount_holds(false, MASTER_UNKNOWN) == false;
}
invariant the_run_and_master_codes_never_move {
assert RUN_MISSING == 0;
assert RUN_FAILURE == 1;
assert RUN_SUCCESS == 2;
assert RUN_CANCELLED == 3;
assert RUN_SKIPPED == 4;
assert RUN_TIMED_OUT == 5;
assert RUN_RUNNING == 6;
assert RUN_OTHER == 7;
assert MASTER_UNKNOWN == 0;
assert MASTER_RED == 1;
assert MASTER_GREEN == 2;
}
invariant unknown_never_discounts {
assert discount_holds(true, MASTER_UNKNOWN) == false;
assert discount_holds(true, run_says(RUN_MISSING)) == false;
assert discount_holds(true, run_says(RUN_CANCELLED)) == false;
assert discount_holds(true, run_says(RUN_SKIPPED)) == false;
assert discount_holds(true, run_says(RUN_TIMED_OUT)) == false;
assert discount_holds(true, run_says(RUN_RUNNING)) == false;
assert discount_holds(true, run_says(RUN_OTHER)) == false;
assert discount_holds(true, look_back(MASTER_UNKNOWN, false, RUN_FAILURE)) == false;
}
Все уроки
Модуль 1 · Задача
Задача становится работой, только когда её оформили: недостающие разделы дописаны или запрошены, граница называет настоящие файлы, а один фильтр читает задачи всех видов.
Модуль 2 · Кто следующий
Порядок, в котором Королева берёт задачи: метка стареет на одну ступень, критичными идут не больше 3, и ничего нового не начинается, пока готовая работа ждёт ревью.
Модуль 3 · Жизнь пчелы
От кандидата до готовой работы: двенадцать состояний и шестнадцать событий, корабль в игре, который улетает домой, и деплой, который не обрывает пчелу.
Модуль 4 · Пчела-ревьюер
Готовую работу судят по своим часам: столько ревью сразу, чтобы успевать, пауза, которая удваивается для строки в ожидании, и ворота слияния, которые не открыть без проверок.
Модуль 5 · Затор ревью
Застрявший пулл-реквест всегда в одном из десяти случаев, у каждого свой ход Королевы, а красная проверка считается унаследованной, только если так говорит master.
Модуль 6 · Ключи
Ключ модели у пчелы могут отклонить: код в теле ответа сортирует отказ, временный отказ даёт ключу отдохнуть секунды, и пчела переходит к следующему свободному ключу.
Модуль 7 · Заслуги и деньги
Одолженный ключ не приносит заслуг, операция продаётся вдвое дороже своей цены в токенах по полцента, а токены сгорают через 30 дней после зачисления.
Модуль 8 · Агенты между собой
У каждого агента свой бот в одной группе: кого зовут, цепочка глубиной в три сообщения ботов и часы, которые не дают агентам завалить чат.
Модуль 9 · Ночь и доска
Что показывает улей: ночной дозор, который открывает, комментирует и закрывает задачи, колонки публичной доски и соты, которые вспыхивают на каждое событие.