rev:pass certifies a head the reviewer has not seen, for up to one dispatcher interval nf-ogds ← Beads

open priority 2 task unassigned

Twinned from https://github.com/cfsh/ninefold/issues/586 by tools/beads/import_github.py's reconcile pass.

tools/reviewer/README.md states the invariant absolutely:

> a green label must never certify a head the reviewer hasn't seen

The mechanism delivers it *eventually*, not never. Labels are re-derived per dispatcher tick, so between a push and the next tick — up to a full interval, ~30m — a PR keeps rev:pass while its verdict covers an older sha.

Observed, today

PR #578 at 17:33Z:

``` head = 1c3a0b4 labels = rev:pass, tier:2 verdict: REVIEWER-VERDICT sha=f8b16e9 ... result=CONVERGED ```

poll.py reported it correctly and loudly — NFR ... [STALE(sha-mismatch)] — because it compares the verdict sha against the live head on every read. The label did not, because nothing recomputes it until the dispatcher next sweeps. Two views of the same fact disagreeing, and only one of them is what a human looks at.

Why it matters more than it sounds

The label *is* the human interface. README.md builds the queue around it:

| 🟢 rev:pass | reviewer PASS or CONVERGED on current head | review/approve this |

and the documented filter is label:rev:pass,rev:escalated. So during the window, a PR whose newest commits no bot has read is sitting in the "ready for you" queue, described as passing *on current head*. Aedan approving on that basis has been told something that is not true — harmless when the push was a typo fix, less so when it was three behaviour changes, which is exactly what mine was.

It is also the one direction the design says it will not go. README.md explains the head-move trigger was widened to every verdict specifically to close *"the seam where an unapproved green PR rebuilt with no comment sat at rev:pending indefinitely — invisible to Aedan (yellow reads as 'wait')"*. That closed yellow-when-it-should-be-green. This is green-when-it-should-be-yellow, which is the more expensive direction.

Not a new mechanism — the reconcile already exists

dispatch.py --reconcile-only <n> does exactly the right thing in two API calls and spawns nothing. The gap is only that nobody runs it at push time. Options:

Unclaimed. Found working #576; not folding it into that chunk.

Dependencies

None.

Comments

No comments.

Add a comment