name: kishore-babysit-prs description: | Async polling layer on top of gstack's greptile-triage helper. Fires after every push to a PR/MR, schedules re-polls per a backoff cadence, walks every review thread (not just the first), classifies via gstack's greptile-triage.md, suppresses against per-project + global greptile-history.md, and stops on two consecutive empty polls.
Forge-aware: GitHub (gh / Pull Request) and GitLab (glab / Merge Request),
detected from git remote.
Use after gh pr create / glab mr create, after every git push to a
branch with an open PR/MR, or when asked to "babysit", "watch greptile",
"poll the PR/MR", "watch reviews", "follow up on review feedback".
Cross-agent: Claude, Codex, OpenCode, Amp.
kishore-babysit-prs
Review bots (greptile on GitHub; greptile or another review bot on GitLab)
post auto-reviews asynchronously. They land as PR review comments / MR
discussion threads, not check runs, so gh pr checks --watch (or the glab
equivalent) doesn't observe them. Without explicit polling on a backoff
cadence, findings arrive after the human stops checking — so they get missed.
This skill is the polling cadence + walk-every-review-thread loop. The
fetch, classify, and reply mechanics live in gstack's greptile-triage.md —
open and follow it; do not paraphrase from memory. This skill never
duplicates the triage helper's logic; it adds (a) the cadence, (b) the
multi-thread walk, and (c) the forge abstraction so the same loop runs on
GitHub and GitLab.
STEP 0 — Open greptile-triage.md every cycle
Before doing anything else in a polling cycle, read the triage helper:
GREPTILE_TRIAGE="$HOME/.local/share/gstack/review/greptile-triage.md"
[ -r "$GREPTILE_TRIAGE" ] || GREPTILE_TRIAGE="$HOME/Projects/dotfiles/.unified-skills/review/greptile-triage.md"
[ -r "$GREPTILE_TRIAGE" ] || { echo "BABYSIT: greptile-triage.md missing — abort cycle"; exit 1; }
Then walk it section by section:
| Triage section | What this skill triggers |
|---|---|
## Fetch |
Run per detected review thread (multi-thread loop below), via the forge layer |
## Suppressions Check |
Read $HOME/.gstack/projects/<slug>/greptile-history.md; skip lines tagged fp matching repo + file-pattern + category |
## Classify |
Read file ±10 lines around each comment's path:line; classify VALID & ACTIONABLE / VALID BUT ALREADY FIXED / FALSE POSITIVE / SUPPRESSED |
## Reply APIs |
Tier 1 first response, Tier 2 on re-flag — via the forge layer |
## Reply Templates |
Use Tier 1 / Tier 2 template verbatim — do not invent new wording |
## Severity Assessment & Re-ranking |
Re-rank the bot's severity against the project's actual risk profile |
## History File Writes |
Append to BOTH per-project and global history files (see below) |
## Output Format |
Use the triage helper's report shape, augmented with our cadence line |
If the triage helper is missing on the host, abort with BABYSIT: greptile-triage.md missing rather than guessing — the abort is visible, a silent re-implementation is not.
STEP 0.5 — Detect the forge
git remote decides which CLI + review-object model to use. Everything
downstream branches on $FORGE.
ORIGIN=$(git remote get-url origin 2>/dev/null)
case "$ORIGIN" in
*github.com*) FORGE=github ;;
*) FORGE=gitlab ;; # gitlab.com OR self-hosted (e.g. awakeninggit.e2enetworks.net)
esac
echo "BABYSIT: forge=$FORGE origin=$ORIGIN"
| Concept | GitHub (gh) |
GitLab (glab) |
|---|---|---|
| Change unit | Pull Request (PR) | Merge Request (MR) |
| Create cmd | gh pr create |
glab mr create |
| Review object | a review with line comments | a discussion (thread) with notes |
| "walk every…" | every review id | every discussion thread id |
| Reply | reply to the review comment | post a note to the discussion |
Both forges' bots are filtered by author name matching greptile (or the
project's configured review bot — see the per-project history bot: note if
present).
Triggers
- After
gh pr create/glab mr createfor a feature branch. - After every
git pushto a branch with an open PR/MR. - User says: "babysit", "watch greptile", "poll the PR", "poll the MR", "watch reviews", "follow up on review feedback", "what did greptile say".
Output contract
Per cycle, print one line (PR on GitHub, MR on GitLab):
BABYSIT <PR|MR> #<n> @ <SHA>: poll <i> | reviews=<count> | new=<m> | actioned=<k> | next=<delay>
actioned = findings whose code fix landed in this cycle.
Cadence (backoff)
| Time since last push (or last review) | Poll interval |
|---|---|
| 0–10 min | +180 s after each push |
| 10–30 min | +300 s |
| 30–60 min | +600 s |
| > 60 min | +1200 s, then stop after 2 consecutive empty polls |
Claude Code: use ScheduleWakeup(delaySeconds, "re-poll review on <PR|MR> #<n> <SHA>"). Other agents: sleep <delay> in a shell loop, or schedule
via the agent's native cron/wakeup mechanism.
Polling loop
GitHub (FORGE=github)
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
PR_NUMBER=$(gh pr view --json number --jq '.number')
# Walk EVERY review id — greptile may post more than one review per push.
REVIEW_IDS=$(gh api "repos/$REPO/pulls/$PR_NUMBER/reviews" \
--jq '.[] | select(.user.login | test("greptile"; "i")) | .id')
for rid in $REVIEW_IDS; do
# Follow greptile-triage.md (STEP 0): fetch line+top-level comments for THIS
# review id; Suppressions Check; Classify; Reply (Tier 1/Tier 2); history write.
# Fetch: gh api "repos/$REPO/pulls/$PR_NUMBER/reviews/$rid/comments"
# Reply: gh api -X POST "repos/$REPO/pulls/$PR_NUMBER/comments/<cid>/replies" -f body="..."
:
done
GitLab (FORGE=gitlab)
# Project full path (e.g. gpu/console), URL-encoded for the API.
PROJECT_PATH=$(glab repo view -F json 2>/dev/null | jq -r '.path_with_namespace' \
|| git remote get-url origin | sed -E 's#.*[:/]([^:/]+/[^/]+)$#\1#; s#\.git$##')
PROJ_ENC=$(printf '%s' "$PROJECT_PATH" | sed 's#/#%2F#g')
MR_IID=$(glab mr view -F json | jq -r '.iid')
# Walk EVERY discussion thread from the review bot. GitLab has no "review"
# object — a bot posts discussion threads (each with notes[]).
THREAD_IDS=$(glab api "projects/$PROJ_ENC/merge_requests/$MR_IID/discussions" --paginate \
| jq -r '.[] | select(any(.notes[]; .author.username | test("greptile"; "i"))) | .id')
for tid in $THREAD_IDS; do
# Follow greptile-triage.md (STEP 0): fetch the thread's notes (body +
# position.new_path/new_line for line-level); Suppressions Check; Classify;
# Reply (Tier 1/Tier 2); history write.
# Fetch: glab api "projects/$PROJ_ENC/merge_requests/$MR_IID/discussions/$tid"
# Reply: glab api -X POST "projects/$PROJ_ENC/merge_requests/$MR_IID/discussions/$tid/notes" -f body="..."
:
done
History paths (forge-independent)
REMOTE_SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null \
|| basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
PROJECT_HISTORY="$HOME/.gstack/projects/$REMOTE_SLUG/greptile-history.md"
GLOBAL_HISTORY="$HOME/.gstack/greptile-history.md"
mkdir -p "$(dirname "$PROJECT_HISTORY")" "$(dirname "$GLOBAL_HISTORY")"
Critical: walk every review thread. Greptile (or the bot) may post multiple reviews/threads per push (one structural, one security, etc.). The triage helper fetches once per call; the multi-thread walk lives here, on both forges.
History file discipline
Every triaged comment writes one line to BOTH:
| File | Purpose |
|---|---|
$HOME/.gstack/projects/<slug>/greptile-history.md |
Per-project. Drives Suppressions Check on next cycle — fp lines suppress matching comments forever. |
$HOME/.gstack/greptile-history.md |
Global aggregate. Cross-project retro / pattern-mining. |
Line format (from the triage helper's "History File Writes" section):
<YYYY-MM-DD> | <owner/repo> | <type:fp|fix|already-fixed> | <file-pattern> | <category>
fp— false positive. Suppressed on future polls.fix— real issue, fixed in this cycle. Not suppressed.already-fixed— real issue, fixed in a prior commit on this branch. Not suppressed.
The categories are a fixed set: race-condition, null-check, error-handling, style, type-safety, security, performance, correctness, other. Don't invent new categories — if a finding doesn't fit, use other and mention the actual concern in the reply, not the history line.
After a fix lands
- Fix the code per the triage helper's classification.
- Re-verify with
make lint+make test(or relevant tier). - Reply via the triage helper's "Reply APIs" section (Tier 1 / Tier 2 templates) through the forge layer above. Include the fix SHA in the body.
- Commit and push.
- Append the history line(s) to both files.
- If the finding generalizes beyond this PR/MR (recurring pattern, new
class of bug), capture as a named rule in the project's
docs/greptile-learnings/RULES.mdin the same commit as the fix. Otherwise the history line alone is the durable record. - Re-schedule the next poll using the cadence table above.
Stop conditions
- Two consecutive empty polls (no new reviews/threads) → stop, report done.
- PR/MR merged or closed → stop, report.
- CI red and not from your changes → surface to the user; pause polling until they decide.
- User says stop / pause → print one final
BABYSIT <PR|MR> #<n>: paused per userline and stop.
Report format
At end of each cycle:
BABYSIT REPORT — <PR|MR> #<n> @ <SHA>
Polls run: <i>
Reviews seen: <count>
Findings actioned: <k> (P0: <a>, P1: <b>)
Findings deferred: <d> (with reasons)
Rules added/updated: <list of RULE refs> # via triage helper's history-write
Next poll: in <delay>s OR stopped (<reason>)
What this skill does NOT do
- Does not re-implement fetch/classify/reply — see
~/.local/share/gstack/review/greptile-triage.md. - Does not merge the PR/MR — use
/land-and-deployafter the bot is green. - Does not modify CI configuration or skip the review bot.
- Does not apply P2/P3/nit findings without explicit user approval (the triage helper's classification drives this).
Failure modes
| Surface | Action |
|---|---|
gh api / glab api rate-limited |
Back off the poll interval by 2× and retry next cycle |
| Review missing on a push | Re-poll once at +180 s; if still missing, surface to user |
gh pr view / glab mr view returns nothing (PR/MR not yet visible) |
Skip cycle; retry next interval |
glab not authed for a self-hosted host |
Surface BABYSIT: glab not authed for <host> — run glab auth login; pause |
| Multiple reviews with conflicting suggestions | Apply the union; surface conflict only if both can't coexist |
| Finding is in code the agent did not author | Surface to user before fixing — never edit changes the agent did not create |
References
~/.local/share/gstack/review/greptile-triage.md— fetch / classify / reply / suppressions / history-write mechanics. Also at~/Projects/dotfiles/.unified-skills/review/greptile-triage.md.$HOME/.gstack/projects/<slug>/greptile-history.md— per-project history; drives Suppressions Check.$HOME/.gstack/greptile-history.md— global aggregate.docs/greptile-learnings/RULES.md— project-level named rules (durable principles). New rule added here only when the finding generalizes; per-incident rows go to greptile-history.md.~/Projects/dotfiles/AGENTS.md— CHORE(close) skill chain step 4 cites this skill.gh apidocs: https://cli.github.com/manual/gh_apiglab apidocs: https://gitlab.com/gitlab-org/cli (glab api --help)