Round 1 review (claude-bot, codex-bot — both raised 1 and 2).
1. `gh issue edit --add-label` rejects the WHOLE call on one unknown
label name, and the blocker:* labels are created only by the
dispatch-only bootstrap. So the first sweep after this lands would
have converged NOTHING on exactly the PRs this change exists to heal,
surfacing only as a WARNING in a cron log. Batching state and blockers
into one edit for anti-flicker is what widened that blast radius.
Every label about to be ADDED is now filtered against the repo's real
label set, read once per sweep; removals need no filter because they
are built from has_label. An unreadable label set does not filter, so
a failed read cannot silently strip the board.
2. blocker:unrequested fired only on MISSING, so a round whose approvals
all staled behind a push — with nothing re-requested — carried no
blocker at all, though the agent owes exactly the same ask. Now
MISSING or STALE: both mean this head has no verdict from that
reviewer.
3. LABELS.md: restore the substantive "Leaves when" text for
state:addressing, and widen the blocker:unrequested row to name both
shapes now that (2) changes what the label means.
Fixtures 66 -> 68.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>