Read each PR's standing state, then the delta. Two things, in this order — the delta alone is a trap: a backlog of unresolved threads that predates the loop never changes, so a delta-only tick reports — while comments pile up unanswered.
a. Standing count (every tick, regardless of change). Per PR, count open review threads — isResolved == false and isOutdated == false — split by author type. One GraphQL call gives both the count and the authors:
gh api graphql -f query='query($owner:String!,$repo:String!,$pr:Int!){
repository(owner:$owner,name:$repo){ pullRequest(number:$pr){
reviewThreads(first:100){ nodes{ isResolved isOutdated
comments(first:1){ nodes{ author{login __typename} } } } } } }
}' -F owner=OWNER -F repo=REPO -F pr=<n>
b. Mergeability + CI (every tick). gh pr view <n> --json mergeable,mergeStateStatus for the merge state, and gh pr checks <n> for the CI rollup — count conclusions (pass / fail / pending / skipping). Report the raw numbers, not a synthesized verdict.
c. Delta. Then diff against last tick — which of those threads, reviews, checks, or mergeable state are new since the previous pass.
Loop state lives in this conversation, not on disk. If a tick has no memory of the last one, re-triaging from scratch is safe: /pr-feedback is read-only, so a repeat pass costs tokens, not correctness. This is the waiting ceiling — accept it rather than building a state store.
Author type is a label, not a filter. Mark an author 🤖 when GraphQL author{ __typename } is Bot, OR the login ends in [bot], OR it's a known bot account (naboo-ai-reviews, cursor/Bugbot); everyone else is 👤. Never drop bot activity — bot findings are shown alongside human ones. This composes with /pr-feedback, which does no author typing of its own.