Twinned from https://github.com/cfsh/ninefold/issues/833 by tools/beads/import_github.py's reconcile pass.
Unclaimed. Filed off a comparison against [codingagentsystem/cas](https://github.com/codingagentsystem/cas), which notifies the operator when a worker hits a blocker. We have no equivalent.
Every outward signal this repo produces fires when a session is done, never when it is stuck.
CLAUDE.md's "primary async signal that a PR is waiting" — fires at gh pr create, i.e. when work is finished and healthy.poll.py is pull-based and runs inside a session. It tells a coder what changed; it tells Aedan nothing unless he is watching that session.grep -ril 'notify\|notification' tools/ --include='*.py' returns nothing. There is no push channel at all.So consider the case CLAUDE.md step 6 explicitly designs for:
> Blocked needing Aedan's approval — the PR touches something outside the perimeter, which --why names by path, not by tier: label. Report the PR URL and stop.
The session reports the URL into its own transcript and halts. If Aedan is not attached to that tmux pane at that moment, the signal is gone. The PR sits in the queue looking like every other open PR, and the fact that it is *parked on a human decision* rather than *converging with the reviewer* is visible nowhere. This is the exact failure mode CLAUDE.md's value 6 warns about in the other direction — "the finding dies in a transcript nobody reads."
Same shape, other blockers:
FAILED with the reason in a 31 KB JSONL.review_now.py lock (#611) — every later call reports a review running when none is.D2 — when you must fail, fail noisily and as soon as possible. Right now a blocked session fails silently, and the cost lands on Aedan's attention rather than a channel (A1).
A push signal on blocked, distinct from the merged/reviewed signals that already exist.
Deliberately *not* specified here — the taker should pick after looking at what the box can actually do:
merge_pr.py --why, and it needs no heuristic (A4: fix the mainline path first).blocked: label + GitHub notification (free, no new infra, and it makes the state visible in the PR queue itself, which is worth something on its own); a comment @-mentioning aedanpope; a desktop notification on the box; the harness's PushNotification. B4 — keep the default simple. The label may be 80% of the value for 5% of the work; if so, ship that and stop.merge_pr.py --why already knows; the question is whether the session announces its own blockage or something sweeps for it. Prefer the former — it is one call at the point the fact is discovered, versus a new poller with its own staleness.Anti-goal: do not build a notification system. One signal for one condition, on the cheapest channel that reaches him. This should not grow a config surface, a severity model, or a second poller. If it wants to become that, it is the wrong shape.
If the derived fleet view (#832) lands, "blocked" becomes a computed badge there for free — and that is the *right* place for the passive display. This issue is about the push half: the case where nobody is looking at any view. Ship them independently; neither blocks the other, and the definition of blocked is shared between them, so whichever lands second should reuse the first's predicate rather than writing a second one. #658 (two readers of claude agents --json) and the #344/#352 perimeter-parser split are the standing examples of what a second implementation of the same state costs here.
None.
No comments.