The Queen's move
You will learn
What the Queen does for each jam, and which jams she waits on.
Each case has one action at the same position: no reviewer dispatches a reviewer, refused not fixed dispatches the coder, refused then pushed dispatches a re-review, and labeled not merged finishes the merge. jam_action maps any value above 9 to measure again, so an unknown case is never nothing to do. jam_is_queens_turn is false for 3 cases, none, draft or fresh, and stacked on an open PR: she waits on those.
Try it
Find the case whose action is wait for the base; then drag to 200 and read the action.

Each jam case has one action at the same position. Drag the case: jam_action names the move, and jam_is_queens_turn splits the board into her turn and waiting.
specs/automation/review-jam.t27
// Review jam: which case a stuck pull request is in, and the Queen's action.
// Part of EPIC #5388. Contract: gHashTag/t27#9032 (re-filed from #5700).
// `tri jam` (gHashTag/999-multibots-telegraf) imports these numbers instead
// of hand-copying them.
module review_jam;
// Jam case constants (u8, value = position, starting at 0)
pub const JAM_NONE: u8 = 0;
pub const JAM_NO_REVIEWER: u8 = 1;
pub const JAM_STACKED_ON_OPEN: u8 = 2;
pub const JAM_REFUSED_NOT_FIXED: u8 = 3;
pub const JAM_REFUSED_THEN_PUSHED: u8 = 4;
pub const JAM_LABELED_NOT_MERGED: u8 = 5;
pub const JAM_CONFLICTING: u8 = 6;
pub const JAM_NOISE_RED: u8 = 7;
pub const JAM_DRAFT_OR_FRESH: u8 = 8;
pub const JAM_UNMEASURED: u8 = 9;
// Queen action constants (u8, value = position, starting at 0)
pub const ACT_NOTHING: u8 = 0;
pub const ACT_DISPATCH_REVIEWER: u8 = 1;
pub const ACT_WAIT_FOR_BASE: u8 = 2;
pub const ACT_DISPATCH_CODER: u8 = 3;
pub const ACT_DISPATCH_RE_REVIEW: u8 = 4;
pub const ACT_FINISH_MERGE: u8 = 5;
pub const ACT_REBASE_BY_CODER: u8 = 6;
pub const ACT_NAME_NOISE_AND_REVIEW: u8 = 7;
pub const ACT_WAIT: u8 = 8;
pub const ACT_MEASURE_AGAIN: u8 = 9;
// The jam case of a pull request. The FIRST matching rule wins:
// 1. not measured -> UNMEASURED
// 2. draft or fresh -> DRAFT_OR_FRESH
// 3. stacked on an open PR -> STACKED_ON_OPEN
// 4. conflicting -> CONFLICTING
// 5. labeled reviewed (and still open) -> LABELED_NOT_MERGED
// 6. refused and pushed after -> REFUSED_THEN_PUSHED
// 7. refused -> REFUSED_NOT_FIXED
// 8. only noise checks red -> NOISE_RED
// 9. no reviewer -> NO_REVIEWER
// 10. otherwise -> NONE
pub fn jam_case(measured: bool, draft_or_fresh: bool, stacked_on_open: bool, conflicting: bool, labeled_reviewed: bool, refused: bool, pushed_after_refusal: bool, has_reviewer: bool, only_noise_red: bool) -> u8 {
if !measured {
return JAM_UNMEASURED;
}
if draft_or_fresh {
return JAM_DRAFT_OR_FRESH;
}
if stacked_on_open {
return JAM_STACKED_ON_OPEN;
}
if conflicting {
return JAM_CONFLICTING;
}
if labeled_reviewed {
return JAM_LABELED_NOT_MERGED;
}
if refused && pushed_after_refusal {
return JAM_REFUSED_THEN_PUSHED;
}
if refused {
return JAM_REFUSED_NOT_FIXED;
}
if only_noise_red {
return JAM_NOISE_RED;
}
if !has_reviewer {
return JAM_NO_REVIEWER;
}
JAM_NONE
}
// The Queen's action for a case: the action with the SAME position.
// Any value above 9 is an unknown case, and an unknown case is never
// "nothing to do": it maps to ACT_MEASURE_AGAIN.
pub fn jam_action(case: u8) -> u8 {
if case > 9 {
return ACT_MEASURE_AGAIN;
}
case
}
// Whether the case is the Queen's turn to act: true for every case except
// NONE, DRAFT_OR_FRESH and STACKED_ON_OPEN.
pub fn jam_is_queens_turn(case: u8) -> bool {
case != JAM_NONE && case != JAM_DRAFT_OR_FRESH && case != JAM_STACKED_ON_OPEN
}
// ---- jam_case: one test per case ----
test "unmeasured beats every other flag" {
assert jam_case(false, true, true, true, true, true, true, true, true) == JAM_UNMEASURED;
}
test "unmeasured with every other flag clear" {
assert jam_case(false, false, false, false, false, false, false, false, false) == JAM_UNMEASURED;
}
test "draft or fresh" {
assert jam_case(true, true, false, false, false, false, false, false, false) == JAM_DRAFT_OR_FRESH;
}
test "stacked on open PR" {
assert jam_case(true, false, true, false, false, false, false, true, false) == JAM_STACKED_ON_OPEN;
}
test "conflicting" {
assert jam_case(true, false, false, true, false, false, false, true, false) == JAM_CONFLICTING;
}
test "labeled not merged" {
assert jam_case(true, false, false, false, true, false, false, true, false) == JAM_LABELED_NOT_MERGED;
}
test "refused then pushed" {
assert jam_case(true, false, false, false, false, true, true, true, false) == JAM_REFUSED_THEN_PUSHED;
}
test "refused not fixed" {
assert jam_case(true, false, false, false, false, true, false, true, false) == JAM_REFUSED_NOT_FIXED;
}
test "noise red" {
assert jam_case(true, false, false, false, false, false, false, true, true) == JAM_NOISE_RED;
}
test "no reviewer" {
assert jam_case(true, false, false, false, false, false, false, false, false) == JAM_NO_REVIEWER;
}
test "none" {
assert jam_case(true, false, false, false, false, false, false, true, false) == JAM_NONE;
}
// ---- jam_case: first-match order, each rule against the next one ----
test "draft beats stacked" {
assert jam_case(true, true, true, false, false, false, false, true, false) == JAM_DRAFT_OR_FRESH;
}
test "stacked and unreviewed is stacked, not no reviewer" {
assert jam_case(true, false, true, false, false, false, false, false, false) == JAM_STACKED_ON_OPEN;
}
test "stacked beats conflicting" {
assert jam_case(true, false, true, true, false, false, false, true, false) == JAM_STACKED_ON_OPEN;
}
test "conflicting beats labeled" {
assert jam_case(true, false, false, true, true, false, false, true, false) == JAM_CONFLICTING;
}
test "labeled beats refused then pushed" {
assert jam_case(true, false, false, false, true, true, true, true, false) == JAM_LABELED_NOT_MERGED;
}
test "refused then pushed beats noise red" {
assert jam_case(true, false, false, false, false, true, true, true, true) == JAM_REFUSED_THEN_PUSHED;
}
test "refused not fixed beats noise red" {
assert jam_case(true, false, false, false, false, true, false, true, true) == JAM_REFUSED_NOT_FIXED;
}
test "noise red beats no reviewer" {
assert jam_case(true, false, false, false, false, false, false, false, true) == JAM_NOISE_RED;
}
test "pushed without a refusal is not refused then pushed" {
assert jam_case(true, false, false, false, false, false, true, true, false) == JAM_NONE;
}
// ---- jam_action: same position, unknown -> measure again ----
test "action none to nothing" {
assert jam_action(JAM_NONE) == ACT_NOTHING;
}
test "action no reviewer to dispatch reviewer" {
assert jam_action(JAM_NO_REVIEWER) == ACT_DISPATCH_REVIEWER;
}
test "action stacked to wait for base" {
assert jam_action(JAM_STACKED_ON_OPEN) == ACT_WAIT_FOR_BASE;
}
test "action refused not fixed to dispatch coder" {
assert jam_action(JAM_REFUSED_NOT_FIXED) == ACT_DISPATCH_CODER;
}
test "action refused then pushed to dispatch re-review" {
assert jam_action(JAM_REFUSED_THEN_PUSHED) == ACT_DISPATCH_RE_REVIEW;
}
test "action labeled not merged to finish merge" {
assert jam_action(JAM_LABELED_NOT_MERGED) == ACT_FINISH_MERGE;
}
test "action conflicting to rebase by coder" {
assert jam_action(JAM_CONFLICTING) == ACT_REBASE_BY_CODER;
}
test "action noise red to name noise and review" {
assert jam_action(JAM_NOISE_RED) == ACT_NAME_NOISE_AND_REVIEW;
}
test "action draft or fresh to wait" {
assert jam_action(JAM_DRAFT_OR_FRESH) == ACT_WAIT;
}
test "action unmeasured to measure again" {
assert jam_action(JAM_UNMEASURED) == ACT_MEASURE_AGAIN;
}
test "action first unknown case measures again" {
assert jam_action(10) == ACT_MEASURE_AGAIN;
}
test "action unknown case 200 measures again" {
assert jam_action(200) == ACT_MEASURE_AGAIN;
}
// ---- jam_is_queens_turn ----
test "queens turn for no reviewer" {
assert jam_is_queens_turn(JAM_NO_REVIEWER) == true;
}
test "queens turn for refused not fixed" {
assert jam_is_queens_turn(JAM_REFUSED_NOT_FIXED) == true;
}
test "queens turn for refused then pushed" {
assert jam_is_queens_turn(JAM_REFUSED_THEN_PUSHED) == true;
}
test "queens turn for labeled not merged" {
assert jam_is_queens_turn(JAM_LABELED_NOT_MERGED) == true;
}
test "queens turn for conflicting" {
assert jam_is_queens_turn(JAM_CONFLICTING) == true;
}
test "queens turn for noise red" {
assert jam_is_queens_turn(JAM_NOISE_RED) == true;
}
test "queens turn for unmeasured" {
assert jam_is_queens_turn(JAM_UNMEASURED) == true;
}
test "not queens turn for none" {
assert jam_is_queens_turn(JAM_NONE) == false;
}
test "not queens turn for draft or fresh" {
assert jam_is_queens_turn(JAM_DRAFT_OR_FRESH) == false;
}
test "not queens turn for stacked on open" {
assert jam_is_queens_turn(JAM_STACKED_ON_OPEN) == false;
}
// ---- the two scenarios of #9032, end to end ----
test "scenario bee-reviewed but still open finishes the merge" {
assert jam_action(jam_case(true, false, false, false, true, false, false, true, false)) == ACT_FINISH_MERGE;
}
test "scenario failed read is never reported as free" {
assert jam_action(jam_case(false, false, false, false, false, false, false, true, false)) == ACT_MEASURE_AGAIN;
}
All lessons
Module 1 · The task
An issue becomes work only once it is shaped: the sections it lacks are written or asked for, its boundary names real files, and one filter reads every kind of task.
Module 2 · Who goes next
The order the Queen takes issues in: a label ages one step, at most 3 criticals run as critical, and nothing new starts while finished work waits for review.
Module 3 · A bee's life
From candidate to done: twelve states and sixteen events, a ship in the game that flies home, and a deploy that does not cut a bee short.
Module 4 · The reviewer bee
Finished work is judged on its own clock: enough reviews at once to keep up, a pause that doubles for a row that waits, and a merge gate that zero checks cannot open.
Module 5 · The review jam
A pull request that will not move is in exactly one of ten cases, each with the Queen's move, and a red check is inherited only when master says so.
Module 6 · Keys
A bee's model key can be refused: the code in the body sorts the refusal, a transient one rests the key for seconds, and the bee moves on to the next free key.
Module 7 · Credit and money
Lending a key earns no credit, an operation is sold at twice its cost in half-cent tokens, and tokens burn 30 days after they land.
Module 8 · Agents among themselves
One bot per agent in one group: who is called, a chain three bot messages deep, and the clocks that keep agents from flooding a chat.
Module 9 · Night and the board
What the hive shows: a night watch that opens, comments on and closes issues, the columns of the public board, and the comb that flashes for each event.