| name | deft-directive-review-cycle |
| description | Greptile / bot reviewer response and PR-shepherding workflow. Use when running a review cycle or babysitting a PR to merge-ready -- audit process prerequisites, fetch bot findings, fix all issues in a single batch commit, and exit cleanly when no P0 or P1 issues remain. Supersedes Cursor global babysit on Deft-managed repos (#2261). |
Deft Directive Review Cycle
Structured workflow for responding to bot reviewer (Greptile) findings on a PR.
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
Platform Requirements
! This skill requires GitHub as the SCM platform and the GitHub CLI (gh) to be installed and authenticated. PR comment fetching, check-run queries, and review submission all depend on gh.
Ordered-plan exit / advance (#2402)
! When the review-cycle exit condition is met (no P0/P1 remaining / CLEAN verdict):
- Return control to the caller — do not discover adjacent backlog work.
- If an ordered-plan sequence is active and this PR matched the current entry, run
task plan-sequence:advance.
- If another entry remains, the caller may continue only to that entry (one unit).
- If the sequence is exhausted, stop and ask. Do not open another PR from queue intuition or chaining text.
⊗ Exit the review cycle into task triage:queue or an unrelated PR without fresh operator approval.
When to Use
- User says "review cycle", "check reviews", or "run review cycle" on a PR
- Operator asks to babysit, shepherd, or watch a PR -- including the Cursor product action babysit-pull-request-in-cloud (#2261 / #1862 intent-routing class)
- A bot reviewer (Greptile) has posted findings on an open PR
- Dispatching a cloud or background agent to monitor and resolve PR review findings until merge-ready
Cursor global babysit supersession (#2261)
On repos with .deft/core/ installed, PR shepherding intent routes here -- not the Cursor-global babysit skill (~/.cursor/skills-cursor/babysit/SKILL.md). The product action babysit-pull-request-in-cloud shares the babysit name but MUST chain into this review-cycle surface on Deft-managed projects.
- ! Orchestrators dispatching cloud/background workers for PR shepherding MUST include
templates/agent-prompt-preamble.md (or reference it) and mandate reading this skill as the first workflow step.
- ! Cloud babysit workers use
worker_role: review-monitor (or drive-to: merge-ready implementation workers that own the review cycle) -- not a generic babysit loop without Phase 1 audit or Step 6 exit predicate (#1259).
- ⊗ Route babysit-pull-request-in-cloud to Cursor global babysit when
.deft/core/ is present -- that bypasses xBRIEF preflight, task check, Greptile fail-closed exit, and CHANGELOG discipline (#2261 recurrence).
Branch-Protection Policy Guard
! Before entering the review/fix loop, run the skill-level branch-policy guard (#746 / #747). Halt before any state mutation if the project's plan.policy.allowDirectCommitsToMaster is unresolvable AND the operator has not set DEFT_ALLOW_DEFAULT_BRANCH_COMMIT=1. Concretely:
task verify:branch || exit 1
The skill MUST NOT modify files, push, or comment on the PR until the guard passes -- this catches the case where a malformed PROJECT-DEFINITION quietly disabled the policy and the agent would have committed directly to master mid-review.
Deterministic Questions Contract
! Every numbered-menu prompt rendered in this skill (Phase 1 audit gates, Phase 2 Step 4 monitoring approach selection, Phase 5->6 ready-to-merge gate, Step 6 exit-condition prompts) MUST follow ../../contracts/deterministic-questions.md: the final two numbered options MUST be Discuss and Back, in that order. The Discuss-pause semantic is documented verbatim there -- on Discuss selection the agent MUST halt the in-progress sequence immediately, prompt What would you like to discuss?, and resume only on an explicit user signal (re-asking the original question, saying resume/continue, or re-issuing the prior selection). Implicit resumption is forbidden.
Pre-Flight Check
! Before entering the review/fix loop, verify the Greptile configuration supports it:
- !
triggerOnUpdates must be enabled (via Greptile dashboard or .greptile/config.json) — without this, Greptile only reviews the initial PR and never re-reviews after fix pushes, so the loop cannot reach the exit condition
- ~
statusCheck should be enabled so Greptile posts a "Greptile Review" check run on each commit — this is the signal the org ruleset uses to gate merges
- ? If Greptile does not re-review after a push despite
triggerOnUpdates being enabled, comment @greptileai on the PR as a manual re-trigger fallback
! Greptile posts check runs (GitHub Checks API), not commit statuses (Statuses API). To verify the check run is present on a commit:
gh api repos/<owner>/<repo>/commits/<sha>/check-runs --jq '.check_runs[] | select(.name == "Greptile Review")'
⊗ Use commits/<sha>/statuses to check for Greptile — that endpoint will always be empty.
~ See tools/greptile.md for recommended dashboard and per-repo settings.
Phase 1 — Deft Process Audit
! Before touching code, verify ALL prerequisites are satisfied. Fix any gaps first:
- ! Verify
skills/deft-directive-pre-pr/SKILL.md was run before PR creation -- the PR branch should have passed at least one full RWLDL cycle. If not, run it now before proceeding.
- !
PROJECT-DEFINITION.xbrief.json and xbrief/ lifecycle folders have scope xBRIEF coverage for all changes in the PR
- !
CHANGELOG.md has entries under [Unreleased] for the PR's changes
- !
task check passes fully (fmt + lint + typecheck + tests + coverage ≥75%)
- !
.github/PULL_REQUEST_TEMPLATE.md checklist is satisfied in the PR description
- ! If the PR touches 3+ files: verify a
/deft:change proposal.xbrief.json exists in history/changes/ for this branch and was explicitly confirmed by the user (affirmative response, not a broad 'proceed'), or document N/A with reason in the PR checklist
- ! Verify the PR is on a feature branch -- work MUST NOT have been committed directly to the default branch (master/main)
~ PR scope gate: If the PR spans 3+ unrelated surfaces (e.g. a skill, a tool doc, and a strategy -- with no shared issue or scope xBRIEF linking them), warn the user that broad PRs increase review churn and Greptile noise. Recommend splitting into focused PRs unless all changes trace to the same scope xBRIEF or issue bundle.
! Phase 1 audit gaps must be resolved before merging — but hold the fixes (do NOT commit or push them independently). Proceed to Phase 2 analysis to gather bot findings, then batch all Phase 1 + Phase 2 fixes into a single commit.
⊗ Commit or push Phase 1 audit fixes independently before gathering Phase 2 findings.
Phase 2 — Review/Fix Loop
Step 1: Fetch ALL bot comments
! Retrieve findings using BOTH methods — each catches different comment categories:
gh pr view <number> --comments
! Use do_not_summarize_output: true — summarizers silently drop the "Comments Outside Diff" section from large bot comments.
~ Oversized output fallback: If do_not_summarize_output: true produces output too large to process, extract the relevant section with:
- PowerShell (Windows):
gh pr view <number> --comments | Select-String "Outside Diff" -Context 50
- Unix/macOS:
gh pr view <number> --comments | grep -A 50 "Outside Diff"
Both commands extract the "Comments Outside Diff" section with surrounding context, avoiding the need to process the full output.
~ Windows + Grok Build (#1353): Avoid |, >, or 2>&1 in run_terminal_command strings -- use Python pathlib/subprocess or plain task commands instead.
! MCP capability probe (mirrors deft-directive-swarm Phase 3 pattern): Before attempting MCP get_review_comments, probe whether MCP GitHub tools are available in the current session. Detection: attempt a lightweight MCP call (e.g. list available tools or a no-op query) -- if it succeeds, MCP is available; if it errors or the tool is not in the available set, MCP is unavailable.
- MCP available: ! Use MCP
get_review_comments as the second source to catch Comments Outside Diff.
- MCP unavailable (e.g. non-MCP agents including
start_agent / spawn_subagent ("grok-build") dispatch, cloud agents, oz agent run): ! Use gh api repos/<owner>/<repo>/pulls/<number>/comments as the explicit fallback for the second review source. Document in the commit message or PR comment why MCP was skipped (e.g. "MCP unavailable in this session -- used gh api fallback for review comments"). The platform descriptor from runtime detection determines MCP availability independently of the dispatch primitive.
⊗ Report "all comments resolved" without verifying both sources.
⊗ Skip the second review source without probing for MCP capability and documenting the fallback used.
~ Late-arriving bot review re-check: If the initial dual-source fetch returns no bot review on the current HEAD SHA, wait ~60s and re-fetch before evaluating the Step 6 exit condition. Bot reviewers (Greptile) typically land within 3-7 min of PR creation/push; an empty first pass is more likely "review pending" than "review clean".
⊗ Declare the exit condition met based on a single fetch that returned no bot review — re-fetch at least once after a ~60s delay first.
~ This codifies a user-rule precedent on late-arriving bot reviews into the deft-internal deterministic tier. The templates/swarm-greptile-poller-prompt.md loop body already handles the same case for push-driven cycles via its per-poll fetch -- the rule above closes the orthogonal cold-start path where the one-shot review-cycle entry runs on a freshly-opened PR before any fix push has triggered the Step 4 polling loop.
Step 2: Analyze ALL findings before changing anything
! Before making any changes:
- Read every finding across all files
- Identify cross-file dependencies (a term, value, or field mentioned in multiple files)
- Categorize by severity (P0, P1, P2 — where P0 is critical/blocking, P1 is a real defect, P2 is a style or non-blocking suggestion)
- Plan a single coherent batch of fixes
⊗ Start fixing individual findings as you encounter them.
Step 3: Fix all findings in ONE batch commit
! Apply ALL fixes across all files before committing:
- ! For any fix that touches a value, term, or field appearing in multiple files: grep for it across the full PR file set and update every occurrence in the same commit
- ! Validate structured data files locally before committing (e.g.
python3 -m json.tool for JSON, YAML lint for YAML) — do not rely on the bot to catch syntax errors
- ! Before committing any Greptile fix, re-read the FULL current Greptile review and confirm all P0/P1 issues are addressed in the staged changes — this is the pre-commit gate that prevents per-finding fix commits
- ! Run
task check before committing
- ! Fail-loud completion claim (#1006): when reporting fix-batch completion (to the user, in the commit message, in a PR comment, or in a status message to a parent agent), MUST surface the OUTCOMES not the intent -- name the P0/P1 finding count addressed ("addressed 3/3 P0 findings, 2/2 P1 findings, 0 deferred" -- NOT "all findings addressed"), report the
task check result with the test-collection counts ("task check: 412 collected, 412 passed, 0 skipped, 0 xfailed" -- NOT "task check passed"), and explicitly call out any finding intentionally deferred with the reason. Apply coding/coding.md ## Fail Loud: Completion Claims Require Outcome Verification (#1006) to every claim emitted during the review cycle
- ⊗ Claim "all Greptile findings addressed" without verifying that the staged fix actually closes every P0/P1 currently surfaced in the review body -- a fix that addresses 3 out of 4 P1 findings and reports completion is the exact failure mode #1006 forbids
- ⊗ Claim "task check passes" when any test was skipped, xfailed, or run with errors suppressed -- report the full collection counts instead (#1006)
- ? Pre-existing failure carve-out: If
task check fails due to a pre-existing issue unrelated to the PR's changes, a partial test suite run is acceptable ONLY if BOTH conditions are met: (a) the task check failure is pre-existing with an open GitHub issue number tracking it, AND (b) the PR description explicitly notes the failure and includes the issue reference (e.g. "task check: test_foo fails due to #NNN (pre-existing)"). Without both conditions, the full task check pass remains mandatory.
- ~ Commit message:
fix: address Greptile review findings (batch)
⊗ Push individual fix commits per finding — always batch.
Step 3b: Proactive test coverage scan
! After committing the fix batch but before pushing, scan the changed lines for untested code paths:
- ! Run
git --no-pager diff HEAD~1 HEAD --name-only to identify files touched in the fix batch
- ! For each changed file that has a corresponding test file, review whether the fix introduced or modified logic that lacks test coverage
- ! If untested code paths are found, write tests and amend them into the fix batch commit (or add as a second commit in the same push)
- ! Run
task check again after adding tests to verify they pass
~ This eliminates one CI round-trip per fix cycle — catching coverage gaps before CI does.
⊗ Push fix commits without scanning for untested code paths in changed files.
Step 4: Push and wait
! Push the batch commit, then wait for the bot to review the latest commit.
! After pushing, the agent MUST autonomously poll for review updates and continue the review cycle without stopping to ask the user. Do not pause for confirmation, do not ask "should I continue?", do not wait for user input between push and review completion. The review/fix loop is designed to run to the exit condition without human intervention.
⊗ Push any additional commits — including unrelated fixes, doc updates, or lessons — while waiting for the bot to finish reviewing the current head. Every push re-triggers Greptile and resets the review clock. If you discover additional work while waiting, stage it locally but do NOT push until the current review completes.
Greptile CLEAN vs CI holdout (pr:watch / #2688)
! When waiting on a Greptile verdict for a drive-to: merge-ready worker (or any review-cycle owner), prefer task pr:watch -- <N> (or --one-shot --json) over ad-hoc sleep loops (#1056). Parse clean_gate_holdout on every probe.
! When clean_gate_holdout=ci_failures and Greptile otherwise satisfies the probe-side Step 6 fields (SHA match on HEAD, confidence > 3, no P0/P1, not errored): MUST NOT idle-poll hoping CI heals. Treat Greptile CLEAN + CI red with the same ownership as a Greptile P0 for a merge-ready worker — one fix batch, re-push, re-probe.
! On persistent ci_failures holdout: exit the Greptile wait immediately, fetch failing check annotations (gh pr checks <N>, CodeQL / required-check details, or ci_failed_checks from pr:watch --json), fix or escalate with evidence, then re-enter the review loop after CI is green.
! PR body "Test plan" checkboxes being [x] do not authorize idle wait — merge-ready is pr:watch CLEAN / the merge path only.
⊗ Treat pr:watch TIMEOUT or long PENDING with clean_gate_holdout=ci_failures as "Greptile still reviewing" — it means CI blocked the clean gate.
~ Surface the holdout to the user/parent on the first stable ci_failures probe (fail-loud), not after burning max-wait-minutes. See also templates/swarm-greptile-poller-prompt.md CLEAN gate evaluation (#1039).
Runner capacity stall (runner_capacity_stall / #2672)
! Framework CI prefers Blacksmith with a timed GH-hosted failover (~20 minute stall budget). When task pr:watch --json / task pr:merge-ready reports ci_ready_state=runner_capacity_stall (or verdict RUNNER_CAPACITY_STALL, exit 2): wait for auto-failover to the ubuntu-latest lane and the authoritative aggregator check. Do not invent --skip-ci or merge with pending required checks.
! runner_capacity_stall is distinct from ordinary not_ready_yet (under budget / in_progress) and from execution hangs (#2652). Capacity stall means required checks stayed queued with no runner claimed past the budget.
⊗ Use --skip-ci / merge-with-pending because CI is capacity-stalled — the failover path is the unblock; skip-ci is an incident-only release escape hatch (#2652), not a runner-capacity remedy.
Stall Detection Rubric (#564)
! Track per poll: startedAt (timestamp of the first observation of the IN_PROGRESS check run for the current commit) and commit.oid (head SHA being reviewed). Both fields MUST be re-recorded every time the head SHA changes -- the rubric measures elapsed time on a single commit, not across the whole review cycle.
! Expected duration baseline -- Greptile reviews typically complete in 2-5 minutes, with 7 minutes as the upper bound of normal. The escalation threshold is 3x expected = ~10 minutes of continuous IN_PROGRESS on the same commit.oid. The 21-minute stall observed during the rc4 swarm cascade on PR #561 is the recurrence record; see ../../meta/lessons.md ## Greptile Review Stall Detection (2026-04).
! When elapsed time on the current commit.oid exceeds 10 minutes (3x expected) without the IN_PROGRESS check transitioning to a terminal state, the agent MUST escalate to the user. The escalation message MUST include: (1) the PR number; (2) the head SHA being reviewed; (3) the elapsed time since startedAt; (4) the four canonical user-decision options.
! User-decision options at escalation -- render as a deterministic numbered menu per ../../contracts/deterministic-questions.md (final two options Discuss + Back):
- Wait another N minutes (user picks N).
- Manually re-trigger Greptile by commenting
@greptileai on the PR (logs the override in a PR comment for auditability per the next rule).
- Skip the bot review for this cycle and exit the loop with a documented reason.
- Cancel the review cycle entirely.
- Discuss.
- Back.
! Auto-restart detection -- when the polling loop observes a NEW startedAt (Greptile dropped its prior check run and started a fresh one without any push from the agent, e.g. service-side restart), the agent MUST reset its elapsed-time clock to the new startedAt AND notify the user that an auto-restart was detected. Resetting the clock without notifying is forbidden -- the user needs to know the cycle effectively re-started.
! INCOMPLETE_BUT_RATED stall signature (#1259): when a poll observes a parsed Confidence Score: X/5 number BUT no terminal check-run (no completed status with a {success, neutral} conclusion) AND/OR no HEAD-matching Last reviewed commit: completion marker, classify the state as INCOMPLETE_BUT_RATED — Greptile has emitted a confidence rating against a review that has NOT terminally landed on the current HEAD. This is NOT an exit condition (the Step 6 fail-closed all-of resolves the missing fields to unknown); treat it as a stall signature and keep polling, escalating per the 10-minute threshold above if it persists. A confidence number is the single most common false-positive for a premature exit — INCOMPLETE_BUT_RATED names the trap so the agent does not mistake a rating for a verdict.
⊗ Auto-retrigger Greptile (empty commits, force-pushes, agent-posted @greptileai comments, status-check rebuilds) without explicit user approval. The escalation menu's option 2 is the ONLY supported re-trigger path, and even that requires the user to pick it.
! Document any user-approved override in a brief PR comment for auditability -- e.g. Note: review-cycle stall detected at <SHA> after <N> min; user approved manual re-trigger via @greptileai per skills/deft-directive-review-cycle Stall Detection Rubric (#564). This makes the override visible to humans reviewing the PR history and to future agents that resume the cycle.
⊗ Treat a stall as silent -- if the elapsed clock crosses the 10-minute threshold the agent MUST surface the menu, even if the agent is mid-poll. Continuing to poll past the threshold without user input is forbidden.
Review Monitoring
! Background / independent dispatch (#1880 Gap D): Long-running review-cycle owners and pollers (>~3 min) MUST be dispatched independently / in the background so the parent conversation stays interactive. On Cursor, use the Task tool background path (run_in_background: true). This generalizes the Approach-1 sub-agent monitor rule to implementation and fix workers as well — foreground dispatch is reserved for short tasks. The parent receives completion via DONE / BLOCKED / FAILED per templates/agent-prompt-preamble.md §11.
! Worker-owns-lifecycle (#1880 Gap C): When this skill runs as part of an implementation worker scoped drive-to: merge-ready, the worker owns the full review/fix loop through merge-ready in its own tool loop — do NOT exit at PR-open expecting the orchestrator to spawn a separate review leaf.
! Swarm monitor must not self-implement (#2843 / Gap C): When a cohort monitor receives BLOCKED or DONE-with-blockers from a drive-to: merge-ready implementation leaf, the monitor MUST NOT enter the review/fix loop inline on Tier 1 — background-dispatch ONE continuation leaf scoped drive-to: merge-ready on the same worktree. Monitor-inline review-cycle is permitted only on Tier 3 or with explicit operator consent (see skills/deft-directive-swarm/SKILL.md Phase 5 completion-notification decision tree).
! Select the monitoring approach based on runtime capability detection (the matrix in skills/deft-directive-swarm/SKILL.md Phase 3 Step 1, extended per #1342 slices 1-2 for spawn_subagent / "grok-build" and per #1877 for Cursor as first-class Tier-1 tiers). Probe the environment (tool set + env vars) to obtain the stable platform descriptor (grok-build, warp-orchestrated, warp-manual, cursor-composer, cursor-cloud-agent, etc.) from the launch adapter / get_platform_capabilities and map the descriptor to the appropriate tier + dispatch primitive (start_agent, spawn_subagent, or the Cursor Task tool). The descriptor (not hard-coded tool presence) is the single source of truth for both launch and review monitoring.
- Tier 1 (orchestrated sub-agent) → Approach 1 (spawn review-monitor sub-agent via the primitive matching the descriptor:
start_agent, spawn_subagent, or the Cursor Task tool with run_in_background: true)
- Tier 2 (no sub-agent primitive, but scheduler/timer/auto-reinvocation) → Approach 2 (yield-between-polls)
- Tier 3 (interactive session, nothing else) → Approach 3 (blocking sleep loop as last resort)
! Detection: use the full runtime capability matrix (swarm Phase 3 + launch adapter from #1342 slice 2). The old single-probe for start_agent is superseded; the returned platform descriptor determines both the orchestration path and the MCP surface (see MCP probe below). If the descriptor is grok-build (spawn_subagent present, start_agent + WARP_* absent), treat as Tier 1 with the spawn_subagent poller path. If the descriptor is cursor-composer / cursor-cloud-agent (Cursor Task tool present, start_agent + WARP_* + spawn_subagent absent), treat as Tier 1 with the backgrounded Cursor Task poller path (#1877) — NOT Tier 3. Cursor's Task tool is a first-class sub-agent primitive; degrading a Cursor session to the Approach-3 blocking poll is the misclassification #1877 closes.
! Swarm agents (whether launched via start_agent or spawn_subagent per the platform descriptor) SHOULD prefer Approach 1 for their own review-monitor sub-agent. Approach 2's yield-between-polls is not self-sustaining for swarm agents (see warning below). Always include the canonical templates/agent-prompt-preamble.md (AGENTS.md read mandate, #810 xBRIEF gate, #798 PowerShell UTF-8, pre-PR + review-cycle mandates) when spawning a poller sub-agent.
! Deterministic review-monitor gate (#2655 / #2814): When Tier 1 is available, run task verify:review-monitor -- --pr <N> [--call-site solo] before yielding, entering Approach 3, or claiming review monitoring started. After spawning Approach 1, claim the PR-anchored lease with task review-monitor:register -- --pr <N> --monitor-agent-id <id> --platform-primitive start_agent|spawn_subagent|cursor-task. Release with task review-monitor:release -- --pr <N> when done. Exit 0 ready / 1 not ready or held-by-other / 2 config. The sole source of truth is the sticky GitHub PR comment (<!-- deft:review-owner -->); legacy .deft/review-monitor.json is obsolete and ignored. On register conflict, attach to the existing owner or stop — do not parallel-fix.
! Regression trigger (#2797): A leaf that claims a monitor is active without a preceding successful task review-monitor:register GitHub claim MUST fail the review-monitor checklist/eval; a backgrounded task pr:watch shell is insufficient.
! CI-holdout carve-out (#2688): When task pr:watch --one-shot --json reports clean_gate_holdout=ci_failures with Greptile fields otherwise satisfied on current HEAD, do not freeze on verify:review-monitor / spawn-monitor as if Greptile latency were the blocker. Fix CI first (same ownership as Greptile P0). Keep or register a review-monitor only while still waiting on Greptile latency; a BLOCKED: ci_failures DONE handback to the implementation owner is correct.
! Cursor leaf boundary (#2797): A Cursor Task leaf cannot reliably spawn another Cursor Task; nested Task (leaf spawning leaf) is unsupported for an Approach 1 review-monitor. A Cursor drive-to: merge-ready leaf MUST either keep ownership in the same agent by blocking on task pr:watch -- <N>, or its envelope MUST instead be stop-at: pr-open so the orchestrator can spawn and register a sibling review-monitor. A background shell task pr:watch is not a monitor and MUST NOT be claimed as one.
Approach 1 (preferred -- sub-agent orchestration available per platform descriptor):
! Background dispatch (#1880): Spawn the review-monitor sub-agent via the matching primitive IN THE BACKGROUND (Cursor: Task run_in_background: true; Grok Build: spawn_subagent with parent yielding). The parent MUST remain interactive while the poller runs.
! Heartbeat contract for Cursor pollers (#1877 / #1166): A Cursor Task review-monitor poller whose loop runs > ~3 min MUST honour the sub-agent heartbeat contract (docs/subagent-heartbeat.md), same as the spawn_subagent path — emit periodic progress so the parent can distinguish a live poller from a hung one.
! When the platform descriptor indicates Tier 1 (sub-agent support), spawn a review-monitor sub-agent using the primitive matching the descriptor:
- ! Launch via the matching primitive:
start_agent (Warp), spawn_subagent (grok-build / TUI / non-Warp), or the Cursor Task tool with run_in_background: true (cursor-composer / cursor-cloud-agent, #1877) with a prompt that instructs it to poll for Greptile completion. For spawn_subagent and the Cursor Task tool the prompt MUST reference the canonical poller template templates/swarm-greptile-poller-prompt.md (with placeholders filled) plus the agent preamble; the working directory / context must be the PR branch (worktree or equivalent for hybrid).
- ! The sub-agent polls using the mechanism for its primitive: for
spawn_subagent use get_command_or_subagent_output (adaptive cadence: ~20-30s first check after push, ~60s second, ~90s thereafter; Greptile typically lands in 3-7 min); for start_agent the native messaging path; for the Cursor Task tool the backgrounded-task completion-notification path. Front-load the first check to catch fast reviews.
- ! When the exit condition is met (Greptile review current on the HEAD commit SHA, confidence > 3, no P0/P1 remaining), the sub-agent reports completion back to the parent (via
send_message_to_agent or the spawn_subagent result channel).
- ! The main conversation pane stays fully interactive during monitoring -- the user (or parent monitor) can continue other work.
- ! On receiving the completion message / result, the parent re-fetches findings (both gh pr view --comments and the secondary source) and proceeds to Step 5.
Approach 2 (fallback -- no sub-agent primitive for the descriptor):
! When the platform descriptor indicates no sub-agent orchestration (or the primitive is unavailable), use discrete tool calls with a yield between checks. For grok-build / spawn_subagent descriptor this path is normally avoided in favor of Approach 1; it exists for pure interactive or limited runtimes.
- ! Use the current shell execution tool (
run_terminal_command or equivalent in the runtime) in wait mode to run gh pr view <number> --comments and gh pr checks <number>.
- ! After each check, yield control (end all tool calls) -- the agent runtime will re-invoke after its interval or on next interaction.
- ! Target adaptive cadence (20-30s / 60s / 90s) where the runtime permits. The full cadence is easiest in Approach 1 (sub-agent) or 3 (blocking); pure yield is runtime-controlled.
- ! No blocking shell pane lock -- the conversation remains interactive between checks.
- ~ Approach 2 requires a periodic re-invocation trigger (timer, scheduler, user nudge, or external orchestrator for hybrid/worktree cases). Without it the poller stops after the first yield.
- ! When the exit condition is met, proceed to Step 5.
⚠️ Swarm / hybrid limitation: Approach 2 is NOT autonomous for swarm agents or manual worktree setups. Yielding ends the turn with no self-wake; the parent monitor (or external scheduler) must detect idle and re-trigger or send a message. For true grok-build / spawn_subagent hybrids, prefer Approach 1 (spawn_subagent + get_command_or_subagent_output poller) exactly as the swarm launch adapter does.
⊗ Use blocking Start-Sleep shell loops or time.sleep() loops EXCEPT as Approach 3 (see below) -- these lock the conversation and prevent user interaction.
⊗ Poll more frequently than every 20 seconds -- use a real delay between checks, not back-to-back calls. Adaptive cadence (20-30s / 60s / 90s) replaces the fixed 60s minimum.
Approach 3 (last resort -- interactive session, no start_agent, no timer/scheduler):
! Approach 3 is a blocking sleep-poll loop used ONLY when both Approach 1 and Approach 2 are unavailable (interactive session with no start_agent and no auto-reinvocation mechanism). Uses PowerShell sleep / Unix sleep commands between polls.
! User warning gate: Before activating Approach 3, the agent MUST warn the user that the conversation pane will be locked during polling and ask for explicit confirmation. Example: "No sub-agent or auto-reinvocation available. I will poll in a blocking loop (~20-30s / 60s / 90s cadence). The conversation will be locked during polling. Proceed? (yes/no)"
⊗ Activate Approach 3 without first warning the user that it will lock the conversation pane.
- ! After receiving user confirmation, use a blocking shell loop with adaptive cadence:
- First check: wait ~25 seconds (e.g.
sleep 25), then poll
- Second check: wait ~60 seconds, then poll
- Subsequent checks: wait ~90 seconds, then poll
- ! Poll using
gh pr view <number> --comments and gh pr checks <number> in the same shell session
- ! When the exit condition is met (Greptile review current, confidence > 3, no P0/P1), exit the loop and proceed to Step 5
- ! If the user interrupts (Ctrl+C or equivalent), exit gracefully and report current review status
! Greptile may advance its review by editing an existing PR issue comment rather than creating a new PR review object. Do NOT rely solely on pulls/{number}/reviews — that endpoint may remain stale at an older commit SHA even after Greptile has reviewed the latest commit.
! To confirm the review is current, check both surfaces:
- PR issue comments (primary signal) — Greptile edits its existing summary comment in place:
gh pr view <number> --comments (with do_not_summarize_output: true)
- Or
gh api repos/<owner>/<repo>/issues/<number>/comments
- Parse the comment body for
Last reviewed commit and compare to the pushed commit SHA
- Check the comment's
updated_at timestamp to confirm it was refreshed after your push
- PR review objects (secondary signal) — may or may not be updated:
gh api repos/<owner>/<repo>/pulls/<number>/reviews
- Check
commit_id on the latest review object
! Treat an edited Greptile issue comment as a valid new review pass even if no new PR review object was created.
! Fetch the full untruncated comment body or use MCP get_comments to get the actual commit URL containing the full SHA — do NOT rely on grepping truncated link text.
⊗ Re-fetch or re-trigger while the bot's last review still targets an older commit on both surfaces.
Step 5: Re-fetch and analyze
! Fetch the new review using both methods from Step 1.
! Analyze all new findings before planning any changes.
Step 6: Exit condition check — fail-closed ReviewerStatus all-of (#1259)
! The loop MAY exit clean ONLY when a SINGLE fresh fetch (not cached state, not a verdict assembled across earlier polls) satisfies ALL of the ReviewerStatus fields below. This is a fail-closed all-of: any field that is missing, unparsed, or ambiguous resolves to unknown, and unknown is NOT a pass — the agent stays in the loop and returns to Step 2. A PARTIAL or STALE Greptile review MUST NOT satisfy the exit predicate; the predicate is what prevents merging un-reviewed code while a P0/P1 finding is still in flight (#1259).
-
! Terminal check-run — the Greptile Review check run on the current HEAD has status == "completed" AND conclusion in {success, neutral}. The conclusions null, cancelled, timed_out, stale, action_required, and failure are explicitly NOT terminal-clean: any of them resolves to unknown and the loop continues. A check run still queued / in_progress is unknown.
-
! HEAD-SHA pinned AT READ TIME — read the current HEAD SHA in the SAME fetch used to evaluate this predicate (gh api repos/<owner>/<repo>/pulls/<number> -q .head.sha, read AT exit-evaluation time, NOT carried over from an earlier poll) and require head_sha_reviewed == current HEAD. A review whose reviewed SHA lags HEAD is unknown.
-
! Completion marker present and matching — the rolling-summary comment body carries Last reviewed commit: <sha> AND that <sha> matches the current HEAD. Extract the SHA with the markdown-link-aware NON-GREEDY regex below. Markdown link text can contain escaped brackets (e.g. a commit subject add \[Unreleased\] entry), so a greedy [^\]]* stops at the first ] and yields no match → false unknown on a clean review (#1326):
Last reviewed commit:\s*\[.*?\]\(https?://github\.com/[^/]+/[^/]+/commit/(?P<sha>[0-9a-f]{7,40})
A missing or non-matching completion marker is unknown. See ../../templates/swarm-greptile-poller-prompt.md ### Last reviewed commit: for the canonical regex shared with the push-driven poller loop.
-
! Confidence > 3 — the parsed Confidence Score: X/5 is strictly greater than 3 (i.e. 4/5 or 5/5). A confidence == 3, an unparsed confidence, or an absent confidence is unknown.
-
! No P0/P1 findings — the triple-tier (+ Tier 2.5) detector reports zero P0 and zero P1 findings (P2 issues are non-blocking style suggestions and do not gate the loop).
! All five fields MUST hold on the SAME single fresh fetch. The agent MUST NOT assemble a "pass" by combining a terminal check-run observed on one poll with a confidence parsed on an earlier poll — the read is atomic per the SHA-pinned-AT-READ-TIME rule above.
? If the bot says "all prior issues resolved" but lists new issues, treat it as one final batch — not the start of another loop. Go back to Step 2 one more time, re-evaluate this all-of, then stop.
⊗ Exit the loop on a confidence number alone while the check run is non-terminal (queued / in_progress / cancelled / timed_out / stale / action_required) — a confidence score is NOT a verdict without a terminal check-run AND a HEAD-matching completion marker (#1259).
⊗ Exit the loop against a reviewed SHA that lags the current HEAD — a partial or stale review MUST resolve to unknown, never to a pass (#1259).
If the exit predicate is not met (any field unknown), go back to Step 2.
Pre-Merge Re-Poll Gate (#1259)
! Immediately before any gh pr merge invocation, the agent MUST re-fetch reviewer state ONE more time — a fresh gh pr view <number> --comments, a fresh gh api repos/<owner>/<repo>/commits/<HEAD>/check-runs, and a fresh HEAD-SHA read — and re-evaluate the Step 6 fail-closed all-of against that fresh fetch. The exit-condition pass recorded at the end of the review loop is NOT sufficient authorization to merge: review state can go stale between the loop's last poll and the merge call (a new push, a Greptile re-trigger, a service-side check-run reset).
! Treat the re-poll and the gh pr merge as an atomic freshness window. If the re-poll shows ANY field unknown, ABORT the merge and return to Step 2.
⊗ Call gh pr merge on the strength of a review verdict observed earlier in the loop without an immediately-preceding re-poll that re-satisfies the Step 6 all-of — merging on cached review state is forbidden (#1259).
Informal-clean missing canonical fields (#1543)
Greptile can post a separate informal clean reply that says prior issues are resolved and the current diff is clean while omitting the canonical rolling-summary fields Directive merge gates require: Last reviewed commit: and Confidence Score: X/5. task pr:merge-ready and task swarm:verify-review-clean correctly refuse merge-ready in this state -- prose alone cannot prove review currency or confidence.
! When the latest Greptile bot comment is found, reports P0=0 and P1=0, but BOTH canonical fields are unparsed, classify the state as informal-clean missing-canonical-fields (see task pr:merge-ready diagnostic output) instead of treating it as "review still writing" or silently polling.
! Recovery for informal-clean missing canonical fields -- route to ONE of these operator actions; do NOT keep polling:
- Comment
@greptileai review on the PR to retrigger a canonical rolling summary on the current HEAD.
- Wait for Greptile to edit its primary rolling-summary comment with both canonical fields, then re-run
task pr:merge-ready -- <N>.
- Document an explicit operator override per
skills/deft-directive-swarm/SKILL.md Phase 6 Step 1 (merge with rationale in the merge commit body).
⊗ Treat informal clean Greptile prose (current diff is clean, looks solid, no new issues) as merge-ready without canonical Last reviewed commit: and Confidence Score: X/5 evidence.
⊗ Keep polling silently when task pr:merge-ready reports the informal-clean missing-canonical-fields diagnostic -- this is a blocked recovery state, not a late-arriving review.
~ Swarm pollers MUST surface this state via the ### (6) INFORMAL-CLEAN terminal exit in templates/swarm-greptile-poller-prompt.md instead of falling through to generic (4) TIMEOUT or (5) STALL.
Submitting GitHub Reviews
! When submitting PR reviews via the GitHub MCP tool, always use pull_request_review_write with method create and the appropriate event:
APPROVE — formally approve the PR (shows green "Approved" status)
REQUEST_CHANGES — block the PR with requested changes
COMMENT — review feedback without approving or blocking
⊗ Use add_issue_comment for review notes — that creates a regular comment, not a formal review. Review notes must always go in the review body via pull_request_review_write.
GitHub Interface Selection
~ Use the most efficient interface for the task:
- MCP GitHub tool — structured/programmatic operations (querying issues, creating PRs, bulk operations, filtering data)
- GitHub CLI (
gh) — quick ad-hoc commands and direct shell integration
Choose whichever minimizes steps and maximizes clarity for the given task.
~ When MCP is unavailable (agents without MCP tools in their dispatch environment, including start_agent / spawn_subagent ("grok-build") cases, cloud agents, oz agent run), gh CLI is sufficient as the sole interface. The dual-source requirement (MCP + gh) in Step 1 applies only when both are available -- agents without MCP access should use gh pr view --comments and gh api as their primary and only review detection surface. Runtime capability detection (swarm Phase 3 matrix) informs both orchestration tier and MCP surface choice.
Framework Events Emitted Here
! When the user replies yes / confirmed / approve on a ready-to-merge PR thread (Phase 5 -> 6 gate per the canonical #642 workflow comment), emit a plan:approved framework event via task lifecycle:event so the approval is captured as a structural artifact rather than prose-only:
task lifecycle:event -- emit plan:approved \
--plan-ref https://github.com/<owner>/<repo>/pull/<N> \
--approver <github-login> \
--approval-phrase <yes|confirmed|approve> \
--pr-number <N>
? Downstream consumers of plan:approved (auto-merge bots, status updates, audit reporting) are explicitly deferred to follow-up work; this event currently emits a record only (#635 events behavioral wiring).
Post-Merge Verification
! After a PR is squash-merged, verify that all referenced issues were actually closed. Squash merges can silently fail to process closing keywords (Closes #N, Fixes #N) from the PR body (#167).
- ! For each issue referenced with a closing keyword in the PR body, run:
gh issue view <N> --json state --jq .state
- ! If the issue state is not
CLOSED, close it manually with a comment referencing the merged PR:
gh issue close <N> --comment "Closed by #<PR> (squash merge — auto-close did not trigger)"
- ~ This step mirrors
skills/deft-directive-swarm/SKILL.md Phase 6 Step 2 and applies to ALL PR merges, not just swarm runs.
- ! For PRs that referenced any umbrella / staying-OPEN issue (
Refs #N), the INVERSE check applies: any protected issue that auto-closed MUST be reopened with a comment citing #701 and the merged PR. See skills/deft-directive-swarm/SKILL.md Phase 6 Step 1 protected-issue reopen sweep and meta/lessons.md ## GitHub Closing-Keyword False-Positive Layer 3 for the persistent closingIssuesReferences link case (Layer 3, #701).
Anti-Patterns
- ⊗ Route PR shepherding to Cursor global
babysit on Deft-managed repos when .deft/core/ is installed -- use this review-cycle skill instead (#2261)
- ⊗ Treat a passing SLizard/Greptile check run, a non-blocking review comment, or an ad hoc fix commit as the review-cycle exit predicate -- Step 6 fail-closed all-of (#1259) and multi-reviewer registry triage (#769) still apply
- ⊗ Push individual fix commits per finding
- ⊗ Start fixing before analyzing ALL findings
- ⊗ Rely on the bot to catch syntax errors in structured data files
- ⊗ Re-trigger a bot review before the previous one has updated
- ⊗ Report "all comments resolved" without checking both
gh pr view --comments and a second source (get_review_comments via MCP, or gh api fallback when MCP is unavailable)
- ⊗ Use
add_issue_comment for formal review submission
- ⊗ Commit or push Phase 1 audit fixes independently — always batch with Phase 2 fixes
- ⊗ Proceed to Phase 2 while any Phase 1 prerequisite is unmet
- ⊗ Rely solely on
pulls/{number}/reviews to detect whether Greptile has reviewed the latest commit — Greptile may update via an edited issue comment instead of a new review object
- ⊗ Push additional commits while Greptile is reviewing the current head — each push re-triggers Greptile and resets the review clock
- ⊗ Use blocking
Start-Sleep shell loops or time.sleep() loops to poll for review updates when Approach 1 or 2 is available -- Approach 3 (blocking loop) is permitted only as a last resort with user warning
- ⊗ Poll more frequently than every 20 seconds -- use a real delay between checks, not back-to-back calls; adaptive cadence (20-30s / 60s / 90s) replaces the fixed 60s minimum
- ⊗ Stop and ask the user whether to continue after pushing -- the review/fix loop MUST run autonomously to the exit condition
- ⊗ Push fix commits without scanning changed lines for untested code paths — always check test coverage before pushing
- ⊗ Push a fix commit that addresses fewer findings than the current Greptile review surfaces — if Greptile flags 3 issues, all 3 must be fixed in one commit before pushing
- ⊗ Push after fixing a P1 without first checking whether the same Greptile review contains additional P0 or P1 findings
- ⊗ Assume squash merge auto-closed referenced issues — always verify with
gh issue view after merge (#167)
- ⊗ Assume Approach 2 (yield-between-polls) produces a self-sustaining polling loop -- yielding ends the agent's turn with no self-wake; swarm agents will silently stop polling
- ⊗ Skip the second review source (MCP or
gh api fallback) without probing for MCP capability and documenting the fallback used
- ⊗ Run a partial test suite instead of
task check without documenting the pre-existing failure reason and open issue number in the PR body
- ⊗ Create a PR without running
skills/deft-directive-pre-pr/SKILL.md first -- the pre-PR quality loop catches issues before they reach the reviewer
- ⊗ Activate Approach 3 (blocking
Start-Sleep loop) without first warning the user that it will lock the conversation pane and receiving confirmation
- ⊗ Exit the review loop on a Greptile confidence number alone while the check run is non-terminal -- a confidence score is NOT a verdict without a terminal check-run (
completed + {success, neutral}) AND a HEAD-matching Last reviewed commit: completion marker (#1259)
- ⊗ Call
gh pr merge on cached/earlier review state without an immediately-preceding pre-merge re-poll that re-satisfies the Step 6 fail-closed all-of (#1259)