Repository navigation
queen: wire conflict_step into the publisher (update-branch / redo conflicting bee PRs) #7573
Description
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: callconflict_stepwith the queued-run count fromgh run list --status queued. - C_UPDATE:
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. - 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-foreignlabel is needed for the glue if it is Python or TypeScript.
Success criteria
t27c typecheck specs/queen/review_valve.t27exits 0- After one publisher run with the queue under 50,
gh pr list --state open --json mergeable,headRefNameshows fewer conflictingqueen-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.
Activity
gHashTag commentedon Oct 7, 2026
Root cause found while measuring (2026-10-07 16:0xZ,
git merge-treeof every conflictingqueen-PR against master): 57 of the 67 conflict only on theirdocs/now/entry.publish.pynames itdocs/now/{today}-published-{slug(title)}.md, andslugcuts at 60 characters, so every "Port gHashTag/BrowserOS trios/agent-server/apps/server/src/..." bee writes the same path (17 PRs share...apps-server-src-l.md). The first one merges; the rest go add/add.conflict_stepis the backstop; this one-line fix removes the main source. The only-t27 gate refuses the.pyedit, so it needs the owner's commit orowner-approved-foreign:now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
29 remaining items
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…
now: unique entry name, so the PR stops colliding with master (Refs #…