feat(loop): needs-human signal — label + notify.sh seam + cockpit "Needs you" strip (issue #99) - #147
Merged
Merged
Conversation
…strip (issue #99) Consolidates the loop's ad-hoc needs-human escalations (attempt-budget, stall) into one shared helper (needs-human.sh: needs_human_flag/needs_human_clear), adds a throttled push-notification seam (notify.sh, empty command = offline no-op), wires it into new block-on-owner points (PR ready for review / re-approval needed in merge-ready.sh, CHANGES_REQUESTED-addressed-awaiting- re-review in pr-feedback.sh) with clears firing on merge/re-dispatch, and surfaces everything as a "Needs you" strip at the top of the cockpit dashboard. Seeds the needs-human label alongside backlog/planned.
…#99 re-review) Both reviewers rejected the original needs-human-signal work: - needs_human_flag now gates the GitHub comment on a FRESH escalation episode, derived from whether the needs-human label is already present on the target (read via `gh pr/issue view --json labels` before mutating). Label add + throttled notify still run every call; only the comment is first-transition-only, so a persisting block-on-owner condition no longer spams a fresh comment every loop tick. Fails open (comment still fires) if the label read itself fails. - pr-feedback.sh gains PR_FEEDBACK_COUNT_ONLY=1, which suppresses every needs_human_flag/needs_human_clear side effect while printing the identical TSV. loop-census.sh's feedback_prs= counter now sets this, so a read-only census can no longer mutate GitHub state; loop-tick.sh's real dispatch invocation is unchanged. - merge-ready.sh's needs-human comment body no longer leaks the raw "SKIP:..." verdict token. - cockpit.sh's "awaiting your review" strip now also includes PRs with no CI checks configured (previously silently omitted). Added merge-ready.test.sh and pr-feedback.test.sh running the REAL scripts (not fakes) against a stubbed bot-gh.sh, asserting the needs_human_flag/clear gh-call sequence including the new comment-gating and count-only behavior. Extended needs-human.test.sh (episode-gating scenarios), loop-ceilings.test.sh (updated gh-call counts for the new label-presence read), and cockpit.test.sh (no-checks PR coverage). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
robercano
approved these changes
Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #99.
Adds a first-class needs-human signal so the loop pushes for the owner's attention instead of idling silently when it blocks on owner judgment.
What's in it
.claude/scripts/notify.sh <severity> <title> <body-line> [--kind K --target T --window S]— adapter-configured notifier seam. Reads thenotifycommand from the active gates.json (GATES_FILE-aware). Empty command = silent no-op, exit 0 (offline/CI safe, same empty-means-skip convention as gates). Passes severity/title/body both positionally and asNOTIFY_SEVERITY/NOTIFY_TITLE/NOTIFY_BODY. Throttled one notification per(kind,target)per window (default 1800s, override via--window/$NOTIFY_THROTTLE_SECONDS), state in.claude/state/notify-throttle.json, degrades gracefully if state is missing/read-only. Alsonotify.sh --clear --kind K --target T..claude/scripts/needs-human.sh— the ONE sharedneeds_human_flag/needs_human_clearseam: idempotently creates + adds theneeds-humanlabel, calls notify, and posts a human-readable comment once per episode (first-transition-gated: comment only when the label was not already present, fail-open on read error).needs_human_clearremoves the label when the condition clears. All best-effort (|| true) so notify/label failures never break the loop.loop-tick.shattempt-budget + stall/resume escalations (refactored off the old ad-hocgh labelcalls);merge-ready.shflags PR-ready-for-review / approval-stale and clears pr-review + changes-requested on merge (also clears the origin issue's attempt-budget/stall via head-branch → issue-number);pr-feedback.shflags changes-requested-addressed-awaiting-re-review and clears when a fresh CR arrives.loop-census.sh's pure counting invocation runs pr-feedback in a newPR_FEEDBACK_COUNT_ONLY=1mode so counting stays side-effect-free.notifykey (empty = disabled) with an inline_notify_notedocumenting the contract + example commands (ntfy / notify-send / webhook curl), scaffolded in BOTH.claude/gates.json(downstream template) and.claude/self/gates.json(this repo's dogfooding; left empty — owner picks their default later). Signal-only semantics — the loop is not paused (per the issue recommendation;planned-removal already provides manual hold).needs-humanadded toseed-issues.shand the setup skill prose.Tests
Offline, fixture-driven
*.test.sh(no tokens/network): newnotify.test.sh,needs-human.test.sh,merge-ready.test.sh,pr-feedback.test.sh; extendedcockpit.test.sh,loop-tick.test.sh,loop-ceilings.test.sh. Cover empty-command no-op, fire-once-then-throttle,--window 0edge, comment episode-gating, count-only side-effect-freedom, and the label add/remove sequences through the real scripts.Gates & review
Self-adapter gates (
GATES_FILE=.claude/self/gates.jsonbuild / lint / test) all green. Reviewed through the self-adapter'scorrectness(opus) andtests(sonnet) lenses, consensus=all — both APPROVE after one fix round (comment-spam gating + census read-only; the tests reviewer mutation-tested the new assertions).🤖 Generated with Claude Code