Skip to content

bc-uxrix: A merge-bead that becomes a card leaves the only list the sweep reads - #779

Open
mordam wants to merge 3 commits into
mainfrom
worktree-card-visibility-uxrix
Open

bc-uxrix: A merge-bead that becomes a card leaves the only list the sweep reads#779
mordam wants to merge 3 commits into
mainfrom
worktree-card-visibility-uxrix

Conversation

@mordam

@mordam mordam commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Gives the merge queue a second read of the tracker so it can see the cards it raised itself, and carves a reviewer's veto out of the reclaim so that visibility cannot merge a refused pull request.

Bd.listAgent runs bd list --exclude-label human — for a good reason of its own — and raiseMergeCard hands a pull request over by putting human on. Those two facts together meant a merge-bead left the only list the sweep ever read at the exact moment it became a card. Nothing failed; two things went quiet. cardedFor selects rows carrying human and not merge-queue, so fed a list human had been filtered out of it could only ever return nothing — the whole take-a-card-back mechanism of bc-91srt had never executed once in the daemon. And strandedPrs builds its cover set from the same rows, so every card came back as a pull request nothing was about, which is the #N is open and no merge-bead is about it line the log has repeated once per tick per card. Both are measured rather than reasoned: replaying the real reads against the live workspace today gives cardedFor 0 from listAgent alone against 21 from both, and strandedPrs 22 false strands against zero.

So Bd.listCards(ws, assignee) is the other half, narrowed by assignee so it is this queue's own cards rather than the inbox read twice, and sweepMergeQueue hands both halves to the reclaim and the strand report while queueFor keeps the queued half alone — putting a card into queued would re-merge every handover. A card list that cannot be read suppresses both consumers rather than running them on a set known to be short.

The veto carve-out had to land in the same change and is Adam's answer on the bead of 2026-08-24: the reclaim is for cards the queue gave up on, not cards a reviewer gave up on. A vetoed card carries both a reviewer's refusal and, underneath it in a different block of the same field, the queue's own stale "waiting on a review" sentence — which is the only reason it was selectable. Without the carve-out, waking cardedFor up would have taken #655 (green, clean, explicitly refused) back and merged it unattended, because gateVerdict does not consult the review gate at all and reviewRequiredPerWorkspace.beadcause is currently false. The predicate is reviewEscalation, already in lib/mergebead.js and already what lib/mergeraise.js escalates on; run against the live cards it fires on exactly bc-0k5bf (#661) and bc-45rd5 (#655) and nothing else. A changes verdict mid-loop is deliberately not carved out, and there is a test either way.

Third, the strand report stops truncating itself in silence: it took pr.list's default of 40 without saying so, and gh sorts newest first, so on 2026-08-24 thirty-five of seventy-five open pull requests could not be reported however stuck they were — and they were the oldest. STRAND_SCAN_MAX is 200, the query costs the same either way, and hitting it is logged. Honest caveat: the repo has 35 open pull requests today, so this half is not currently observable in production and is pinned by a test instead. I also widened the directory set the report sweeps to include repos known only through a card, which the bead did not ask for but is the same "a card is a merge-bead too" rule — without it, a workspace whose merge-beads had all been carded reported nothing at all.

The test change is the part I would look at hardest. fakeBd.listAgent in test/mergequeue.mjs now applies the same --exclude-label human the real one does, which is what makes the five existing reclaim tests fail honestly if the second read is ever removed; they had been passing against a wiring that could not deliver them a row. A new test deletes bd.listCards and asserts nothing is reclaimed, so the dependency is falsifiable rather than implied. lib/inmain.js carried a comment asserting listAgent is the queue's only read — amended rather than deleted, because its own exclusion is still right: an inmain flag leaves merge-queue on, so such a bead is still unmergeable and still unreclaimable, it is merely counted as cover now.

Tests: affected: 227 suites for 7 changed files, all passed (node bin/b7e-shipgate). Plus a read-only replay of the real read against the live workspace: cardedFor(listAgent only)=0 vs cardedFor(both)=21, strandedPrs 22 false strands -> 0.

Worth knowing: The first tick after this merges takes 12 cards back onto the queue. Measured by replaying cardedFor -> pr.view -> gateVerdict against live state: of 21 selected, 1 is already MERGED, 8 stay cards on their own gate, 12 say merge. That is bc-91srt working for the first time and it is intended, but nobody has ever watched it run. Merges are still paced by MERGES_PER_TICK=2 and each card is re-asked at most once per RECLAIM_COOLDOWN_MS, so it is a label swap on 12 beads rather than 12 merges in a minute. There is deliberately no per-tick cap on the reclaim itself; if 12 at once proves too many, the shape of that fix is a MERGES_PER_TICK sibling in the reclaim loop.

Left undone: No cap on reclaims per tick (see the risk). queueFor is still fed the queued half only, on purpose. The blind spot strandedPrs already names - a workspace with no merge-bead of any kind, so no checkout is resolvable - is narrowed but not closed: a card now names a directory, so only a repo with neither a queued bead nor a card is invisible.

Files changed — 8 files · +474 −15 · against `main`
test/mergequeue.mjs      +182 −4
lib/mergequeue.js        +101 −5
README.md                +61 −0
lib/mergeadvocate.js     +44 −1
test/lapsedrefusals.mjs  +41 −1
lib/bd.js                +32 −0
lib/inmain.js            +9 −4
test/bdtimeout.mjs       +4 −0

Opened by a beadcause worker session on bc-uxrix — A merge-bead that becomes a card leaves the only list the sweep reads, so cardedFor can never reclaim one and strandedPrs calls every card a strand. It merges itself once the checks report; merging is what closes the bead. If this is still open, something stopped that, and the reason is on bc-uxrix and in Adam's inbox.

bead: bc-uxrix

`bd.listAgent` runs `--exclude-label human` and `raiseMergeCard` puts `human`
on, so a merge-bead left the only list the sweep read the moment it became a
card. Two things went quiet: `cardedFor` could only ever return `[]`, so the
take-a-card-back loop of bc-91srt had never run in the daemon; and
`strandedPrs` built its cover set from the same rows, so every card was
reported as a pull request nothing was about.

Adds `Bd.listCards` — a second read narrowed by assignee to this queue's own
cards — and hands both halves to the reclaim and the strand report. Carves a
reviewer's veto out of the reclaim per Adam's 2026-08-24 answer, without
which making cards visible would merge a refused pull request unattended.
Raises the strand scan off `pr.list`'s silent default of 40 and says when the
ceiling is hit.

`fakeBd.listAgent` in test/mergequeue.mjs now applies the same exclusion the
real one does, so the five reclaim tests fail honestly if the second read is
ever removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mordam

mordam commented Aug 26, 2026

Copy link
Copy Markdown
Owner Author

A beadcause worker opened this and does not merge its own work. It is on the merge queue as bc-zaqxb.

@mordam

mordam commented Aug 26, 2026

Copy link
Copy Markdown
Owner Author

The beadcause merge queue tried to merge this and could not: it has been tried 3 times and stopped at the same place each time. nothing ran on this commit at all — zero checks were reported for it, so there is nothing here that says this passed. Tried 3 times — that was the last. It is Adam's call now — see bc-zaqxb.

…ity-uxrix

# Conflicts:
#	test/mergequeue.mjs
@NeanderthalMan

Copy link
Copy Markdown
Contributor

beadcause-resolver: stood down — the worktree for worktree-card-visibility-uxrix is already locked by another resolver ("resolver pid 8202 #779", a live claude process started 16:12 ADT), so I did not touch the tree. Branch left as-is; still CONFLICTING with main.

@NeanderthalMan

Copy link
Copy Markdown
Contributor

beadcause-resolver: stood down again — the worktree is still locked "resolver pid 8202 #779" (that claude has been alive since 16:12 ADT, ~2h20m, tree clean, no MERGE_HEAD, HEAD still 497c6af). This is the second resolver to stand down on the same lock; the holder may be stuck rather than working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants