Context
#7571 / PR #7572 put the decision for a conflicting bee PR in specs/queen/review_valve.t27 (conflict_step). Nothing calls it yet, so the 67 conflicting queen- PRs (2026-10-07 15:51Z) stay stranded. tools/queen/pusher.py already counts conflicted_bee_prs, and tools/queen/publish.py already skips a conflicting branch at publish time. Neither handles a PR that goes into conflict after it was opened.
Boundary
The publisher's scheduled run, tools/queen/publish.py or its workflow: thin I/O glue only. The decision is read from gen/c/queen/review_valve.c (or t27c gen-js), never restated. The supervisor (BrowserOS queen-tick.ts) releases a dispatch on C_REDO through its existing failed-release path. BrowserOS merges are the owner's.
Scenarios
- Each tick, for every open PR whose head starts with
queen-, mergeable == CONFLICTING: call conflict_step with the queued-run count from gh run list --status queued.
- C_UPDATE:
PUT /repos/gHashTag/t27/pulls/N/update-branch with the t27-bees App token. With GITHUB_TOKEN, the merge commit starts no workflow, so the head would sit without checks. A 422 is recorded as update_failed.
- C_REDO: close the PR with a comment naming the reason and the update count; release the dispatch so a fresh bee takes the issue from master.
- C_CLOSE: close the PR and mark the dispatch obsolete, as STEP_CLOSE does.
- C_WAIT: nothing, logged with the queue count.
Requirements
- Counters (
updates, first-seen-conflicting time) live where the dispatch row lives, not in the PR body.
- Never force-push and never rebase locally: update-branch is the only write to the head.
- An
owner-approved-foreign label is needed for the glue if it is Python or TypeScript.
Success criteria
t27c typecheck specs/queen/review_valve.t27 exits 0
- After one publisher run with the queue under 50,
gh pr list --state open --json mergeable,headRefName shows fewer conflicting queen- PRs than before the run.
Requirements
FR-001: The publisher MUST call conflict_step from gen/c/queen/review_valve.c for every open PR whose headRefName starts with queen- and whose mergeable state is CONFLICTING, passing the queued-run count from gh run list --status queued.
FR-002: On C_UPDATE the publisher MUST call PUT /repos/gHashTag/t27/pulls/{number}/update-branch using the t27-bees App token and MUST record a 422 response as update_failed.
FR-003: On C_REDO the publisher MUST close the PR with a comment that includes the reason and the update count, and MUST release the dispatch so a fresh bee can take the issue from master.
FR-004: On C_CLOSE the publisher MUST close the PR and MUST mark the dispatch obsolete.
FR-005: On C_WAIT the publisher MUST perform no write and MUST log the queue count.
FR-006: Counters (updates, first-seen-conflicting time) MUST be stored in the dispatch row, not in the PR body.
FR-007: The publisher MUST NOT force-push and MUST NOT rebase locally; update-branch is the only write to the head.
FR-008: If the glue code is Python or TypeScript, it MUST carry the owner-approved-foreign label.
FR-009: The publisher MUST read the conflict decision from gen/c/queen/review_valve.c (or t27c gen-js) and MUST NOT restate the decision logic.
Context
#7571 / PR #7572 put the decision for a conflicting bee PR in
specs/queen/review_valve.t27(conflict_step). Nothing calls it yet, so the 67 conflictingqueen-PRs (2026-10-07 15:51Z) stay stranded.tools/queen/pusher.pyalready countsconflicted_bee_prs, andtools/queen/publish.pyalready skips a conflicting branch at publish time. Neither handles a PR that goes into conflict after it was opened.Boundary
The publisher's scheduled run,
tools/queen/publish.pyor its workflow: thin I/O glue only. The decision is read fromgen/c/queen/review_valve.c(ort27c gen-js), never restated. The supervisor (BrowserOSqueen-tick.ts) releases a dispatch on C_REDO through its existing failed-release path. BrowserOS merges are the owner's.Scenarios
queen-,mergeable == CONFLICTING: callconflict_stepwith the queued-run count fromgh run list --status queued.PUT /repos/gHashTag/t27/pulls/N/update-branchwith the t27-bees App token. WithGITHUB_TOKEN, the merge commit starts no workflow, so the head would sit without checks. A 422 is recorded as update_failed.Requirements
updates, first-seen-conflicting time) live where the dispatch row lives, not in the PR body.owner-approved-foreignlabel is needed for the glue if it is Python or TypeScript.Success criteria
t27c typecheck specs/queen/review_valve.t27exits 0gh pr list --state open --json mergeable,headRefNameshows fewer conflictingqueen-PRs than before the run.Requirements
FR-001: The publisher MUST call
conflict_stepfromgen/c/queen/review_valve.cfor every open PR whose headRefName starts withqueen-and whosemergeablestate isCONFLICTING, passing the queued-run count fromgh run list --status queued.FR-002: On
C_UPDATEthe publisher MUST callPUT /repos/gHashTag/t27/pulls/{number}/update-branchusing the t27-bees App token and MUST record a 422 response asupdate_failed.FR-003: On
C_REDOthe publisher MUST close the PR with a comment that includes the reason and the update count, and MUST release the dispatch so a fresh bee can take the issue from master.FR-004: On
C_CLOSEthe publisher MUST close the PR and MUST mark the dispatch obsolete.FR-005: On
C_WAITthe publisher MUST perform no write and MUST log the queue count.FR-006: Counters (
updates, first-seen-conflicting time) MUST be stored in the dispatch row, not in the PR body.FR-007: The publisher MUST NOT force-push and MUST NOT rebase locally;
update-branchis the only write to the head.FR-008: If the glue code is Python or TypeScript, it MUST carry the
owner-approved-foreignlabel.FR-009: The publisher MUST read the conflict decision from
gen/c/queen/review_valve.c(ort27c gen-js) and MUST NOT restate the decision logic.