feat(bridge): poll gh for PR/issue status changes (ccc-node#962) - #966
Conversation
Closes the poll half of the gap identified in #962: a session that opens a PR from a bridge identity had no push (webhook) or poll path to learn later that the PR's state changed. #906 sat closed as a duplicate 44 minutes after opening, CI passing 6 minutes in, but the session kept reporting "CI still running" for two days because nothing ever told it otherwise and hand-off carried the stale claim forward unverified. Adds ccc-pr-status-poll.sh: tracks operator-configured "<owner/repo> <author>" pairs' open PRs, diffs against a state snapshot, and notifies (spool only, same as ccc-self-update.sh) on a check-rollup transition into SUCCESS/FAILURE or a previously-open PR closing/merging. First sighting of a repo/author pair seeds silently — nothing to diff against yet, so no notification burst on rollout. A live smoke test against the actual ccc-node#965 PR caught a real bug before merge: CodeQL legitimately concludes NEUTRAL (not SUCCESS) on a clean run, and an all-conclusions-must-equal-SUCCESS comparison misreported a fully green, fully COMPLETED PR as still PENDING. Fixed to separate "is it done" (status/state) from "was it bad" (FAILURE/CANCELLED/TIMED_OUT/ACTION_REQUIRED/STARTUP_FAILURE/ERROR), with a regression test locking it in. Also adds install-pr-status-poll-cron.sh (dry-run-by-default crontab installer, mirrors install-memory-refresh-cron.sh) and wires setup.sh to deploy the script into ~/.claude/hooks/, matching the ccc-self-update.sh pattern. Deliberately out of scope, per the issue's own proposal split: the webhook path (gongyung has no public inbound endpoint) and a session hand-off pre-flight revalidation hook (touches shared session-start logic used by every session — worth revisiting once this proves out). scripts/ccc-pr-status-poll.test.sh: PASS=22 FAIL=0 scripts/install-pr-status-poll-cron.test.sh: PASS=16 FAIL=0 (both suites needed a real bash path in their fake-binary shebangs instead of #!/usr/bin/env bash — this sandbox has no /usr/bin/env, same constraint already documented in bridge/start.sh; pre-existing suites hit the same gap and were left as-is, out of scope here) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
seoseo-ai
left a comment
There was a problem hiding this comment.
Self-review: 6 files, all new except setup.sh (2 additive cp/chmod lines, no existing lines touched). No secrets, no destructive ops — script only reads gh PR state and writes to its own state/spool files. Caught and fixed a real bug pre-merge via live smoke test against this repo's own PR #965 (CodeQL NEUTRAL conclusion was misclassified as PENDING); regression test added (case 7 in ccc-pr-status-poll.test.sh). Both new test suites green (22/22, 16/16); setup.sh --dry-run confirms clean integration. Already deployed live on gongyung (cron installed, first poll run seeded state for #966/#965/#959 with no notification burst) ahead of merge since it's read-only against GitHub — this PR is the durable/reviewable record of that change. CI green (validate-harness, bridge-tests 3.11/3.12, python-lint, secret-scan, wheel-smoke all SUCCESS; CodeQL NEUTRAL as expected).
jinon86
left a comment
There was a problem hiding this comment.
Approved by owner (jinon86): ccc-node#962 poll implementation, CI green, tested (22+16 tests), real bug caught+fixed pre-merge via live smoke test, already deployed on gongyung.
Summary
Closes the poll half of the gap identified in #962: a session that opens a PR from a bridge identity had no push (webhook) or poll path to learn later that the PR's state changed. #906 sat closed as a duplicate 44 minutes after opening, CI passing 6 minutes in, but the session kept reporting "CI still running" for two days because nothing ever told it otherwise, and hand-off carried the stale claim forward unverified.
scripts/ccc-pr-status-poll.sh— tracks operator-configured<owner/repo> <author>pairs' open PRs (~/.claude/pr-status-poll.repos), diffs against a state snapshot (~/.claude/state/pr-status-poll.json), and notifies (spool only — never touches the bot token, same asccc-self-update.sh) on:SUCCESS/FAILUREscripts/install-pr-status-poll-cron.sh— dry-run-by-default crontab installer, mirrorsinstall-memory-refresh-cron.sh.setup.shwired to deploy the script into~/.claude/hooks/, matching theccc-self-update.shpattern.docs/pr-status-poll.md.Deliberately out of scope (per the issue's own proposal split):
A real bug caught before merge
A live smoke test against the actual
ccc-node#965PR (this session's own prior PR) caught a real bug: CodeQL legitimately concludesNEUTRAL(notSUCCESS) on a clean run, and an "all conclusions must equal SUCCESS" comparison misreported a fully green, fullyCOMPLETEDPR as stillPENDING. Fixed to separate "is it done" (status/state) from "was it bad" (FAILURE/CANCELLED/TIMED_OUT/ACTION_REQUIRED/STARTUP_FAILURE/ERROR), with a regression test locking it in (ccc-pr-status-poll.test.shcase 7).Test plan
bash scripts/ccc-pr-status-poll.sh— PASS=22 FAIL=0bash scripts/install-pr-status-poll-cron.test.sh— PASS=16 FAIL=0bash -non all new/changed files./setup.sh --dry-run— new file appears in the copy/chmod plan, no errorsjinwon-int/ccc-node(authorseoseo-ai) in scratch state/spool dirs — realghdata, correctly seedsSUCCESS/PENDINGper open PR, no notification burst on first run*/17 * * * *poll interval is reasonable fleet-wide (only takes effect once an operator runsinstall-pr-status-poll-cron.sh --applyon a given node — installing the script alone changes no node's behavior)Note: both new test suites needed a real resolved
bashpath in their fake-binary shebangs instead of#!/usr/bin/env bash— this sandbox has no/usr/bin/env(same constraint already documented inbridge/start.sh). Several pre-existing suites (ccc-self-update.test.sh,bridge/restart.test.sh,install-memory-refresh-cron.test.sh) hit the same gap already and were left as-is here — confirmed viagit stashthat failure counts are identical before/after this PR's changes, so nothing here is a regression.🤖 Generated with Claude Code