deftai

deft-directive-review-cycle

Greptile / GitHub adapter for coding/review.md: babysit a PR to merge-ready — audit process prerequisites, fetch bot findings, fix in one batch commit, exit when no P0/P1 remain. Supersedes Cursor global babysit on Deft-managed repos (#2261 / #1471).

deftai 31 14 Updated 1w ago
GitHub

Install

npx skillscat add deftai/directive/deft-directive-review-cycle

Install via the SkillsCat registry.

SKILL.md

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.

Principle Authority (#1471 / #212)

! Universal review-cycle principles live in `coding/review.md` (RFC2119): read-all-findings, severity P0/P1/P2 + merge-blocking semantics, single batch commit, cross-file grep of changed terms, local structured-data validation (SHOULD), no push mid-review, exit when no P0/P1 remain (P2 non-blocking), and post-merge closing-keyword verification.

! This skill is the Greptile + GitHub adapter only: polling cadence, confidence-score thresholds, check-run detection, dual-source fetch (gh + MCP) for Comments Outside Diff, fail-closed Step 6 ReviewerStatus, review-monitor lease, and merge gates. Apply coding/review.md for the process; do not restate that principle list inline.

~ Future adapters (CodeRabbit, Codacy, host babysit) SHOULD implement the same coding/review.md principles with their own mechanics.

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):

  1. Return control to the caller — do not discover adjacent backlog work.
  2. If an ordered-plan sequence is active and this PR matched the current entry, run task plan-sequence:advance.
  3. If another entry remains, the caller may continue only to that entry (one unit).
  4. 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
  • Operator re-authorizes after conf-hold / dual-stop residual: pursue residual, follow-up hard-stop, same as conf-hold, continue dual-stopped PR, or re-babysit residual — route to § Operator follow-up after dual-stop / hard stop (#3273) one-shot path
  • Operator issues a standing residual order: until floor or loop, until greptile meets policy, or pursue residuals until told otherwise — route to the same § standing path (#3448); applies to open cohort / ordered-plan units, not only the last halted PR

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).

OpenClaw babysit supersession (#2876 / #2261)

On OpenClaw hosts with Directive installed (.deft/core/ or equivalent content deposit), babysit / shepherd / watch intent likewise routes here -- not host-local freestyle poll loops and not OpenClaw cron alone.

  • ! When the OpenClaw tool surface exposes sessions_spawn, Approach 1 is the default babysit path: spawn a background review-monitor via sessions_spawn (register with --platform-primitive sessions_spawn or the alias openclaw-sessions-spawn).
  • ! Prefer a visible Control UI subagent (visible:true when the tool surface allows) when OpenClaw Control UI is the operator control plane so humans can inspect the monitor (#3044; invisible empty settles are higher FC04 residual risk).
  • ! Long review-monitor ownership (>~3 min) MUST NOT block the parent OpenClaw session — background sessions_spawn + parent yield; same Gap D rule as Cursor/Grok Build (#1880).
  • ! Prefer deep-think gates inside the monitor via the dual-invoke probe order (#2893): deft pr:watch / deft pr:merge-ready first, then task deft:pr:watch when the Taskfile include is present, then the #2878 gh-only fallback — bare task pr:watch is not the consumer form.
  • ⊗ Treat OpenClaw cron (or any host scheduler alone) as Approach 1. Cron/timer re-invocation is Approach 2 only when sessions_spawn is unavailable.
  • ⊗ Freestyle main-session gh pr view / sleep poll + ad-hoc cron when sessions_spawn is available — that is the statusreport#153 / #2876 process-routing failure mode.

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:

deft 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:

  1. ! 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
  2. ~ 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
  3. ? 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:

  1. ! Verify skills/deft-directive-pre-pr/SKILL.md was run before PR creation -- the PR branch should have passed at least one full pre-PR cycle (Read-Write-Lint-Diff). If not, run it now before proceeding.
  2. ! PROJECT-DEFINITION.xbrief.json and xbrief/ lifecycle folders have scope xBRIEF coverage for all changes in the PR
  3. ! CHANGELOG.md has entries under [Unreleased] for the PR's changes
  4. ! task check passes fully (fmt + lint + typecheck + tests + coverage ≥75%)
  5. ! .github/PULL_REQUEST_TEMPLATE.md checklist is satisfied in the PR description
  6. ! 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
  7. ! 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

! Apply `coding/review.md` before any code change: read all findings, classify P0/P1/P2, and plan one batch. Do not start per-finding fixes mid-read.

Greptile adapter analysis extras:

  • Identify cross-file dependencies (a term, value, or field mentioned in multiple files)
  • ! Scope-check each finding against the active story xBRIEF (plan.items[].narrative.Acceptance) before coding — see Out-of-AC gate (#2881) below

Out-of-AC findings / active-story scope gate (#2881)

Babysit and review-cycle are not a second unbounded implementation mandate. When Greptile (or another reviewer) raises design-adjacent work outside the active story's acceptance criteria, default to follow-up — not silent redesign mid-babysit (PR #2871 / #2869 → #2880 recurrence).

! Given an active story xBRIEF with fixed plan.items, when a finding is outside those Acceptance criteria:

  1. ! File or propose a follow-up GitHub issue / scope xBRIEF for the out-of-AC work, or
  2. ! Amend the active brief's plan.items (and narratives) with explicit operator consent before implementing a new subsystem,
  3. ! Then continue babysit only for in-AC and carve-out findings.

! P0 carve-out vs new-subsystem-needs-story:

  • ! P0 security / correctness defects on files already touched by the PR MAY land in the same PR without a new story.
  • ! New ledgers, idempotency protocols, cross-cutting reliability contracts, or multi-commit redesigns that expand story meaning require a separate story or an amended brief before code.

Leftover classes (A/B/C, #3448):

Class What it is Auto-continue?
class A — named leftover on already-touched files Wrong remediation, dropped field, fixture, same-module hole on files this PR already owns in-AC residual. Continue until the resolved #3095 floor or same-fingerprint loop
class B — new subsystem / AC fight New ledger, protocol, cross-cutting contract, or work that expands story meaning Park or file follow-up. Do not expand mid-babysit
class C — score-only, no concrete finding Confidence below the resolved floor, 0 P0/P1, no named leftover Document / same-as-conf-hold / operator floor this-PR-only. Not unbounded redesign

! Already-touched leftover is class A (#2881 / #3448): a leftover on files already in the PR is class A (in-AC residual) unless it needs a new ledger, protocol, or story.

! Confidence-only holds (0 P0/P1): when confidence is below the resolved #3095 floor (plan.policy.review.minGreptileConfidence: typed project policy > framework dogfood 5 > consumer default 4; inspect task policy:show --field=minGreptileConfidence / deft policy:show --field=minGreptileConfidence) with zero P0 and zero P1 findings, the skill does not mandate unbounded redesign to raise confidence. That is class C unless a named leftover on already-touched files makes it class A. Offer one of:

  1. Follow-up issue / residual-risk note in the PR,
  2. Operator override path (document in PR comment),
  3. Minimal in-AC polish only — then re-evaluate Step 6.

! issue:emit related-ref footgun: task issue:emit treats any plan.references[] entry whose type matches github-issue / x-xbrief/github-issue / x-vbrief/github-issue as already tracked and SKIPs creating a new issue. When linking related (non-primary) work:

  • ! Use a non-emit-tracking type for related issues (e.g. keep related links in narratives / PR body / Refs #N prose), or designate a single primary origin github-issue ref that emit should honor.
  • ⊗ Add related-issue x-*/github-issue refs to a brief that still needs issue:emit for its primary origin — emit will SKIP as already tracked.

⊗ Expand active story scope past xBRIEF AC mid-babysit without follow-up or consented amend (#2881).
⊗ Treat confidence-only holds as authorization to invent new subsystems in-tree.

Dual stop — review fix loops (#2442)

Review fix cycles are multi-iteration work and MUST carry dual stop (main.md ## Dual Stop Rule (#2442)). Single-turn re-checks are exempt.

Defaults for this skill (override only with an explicit operator envelope):

Loop class Success stop Default failure stop
Greptile / bot fix batch (Step 3 → re-review) No P0/P1 on current HEAD; confidence meets minGreptileConfidence max 3 fix-batch iterations across the whole review ownership (do not reset the counter on push when the same primary fingerprint remains) or the Same-fingerprint stop (below)
Confidence-only hold (0 P0/P1, score below floor) Confidence meets floor, or operator chooses document/accept path max 1 optional polish pass, then stop (do not redesign unbounded — see confidence-only holds above)

On failure stop:

  • ! Halt automatic re-fix. Prefer BLOCKED: with PR number, HEAD SHA, blocker class (review_cycle_cap / greptile_p0_p1 / conf_floor / no_progress), what was tried, and human decision needed (preamble §11 / #2843).
  • ! Halt-report resume line (MUST, #3273 / #3448): End the terminal halt with leftover class (A/B/C) + residual class + conf (if any) + resolved #3095 floor + standing vs one-shot + PR URL. Example: RESUME: residual=<class> leftover=<A|B|C> conf=<n/5|n/a> floor=<resolved min|n/a> standing=<yes|one-shot> PR=<url>. One-shot phrases: pursue residual | follow-up hard-stop | same as conf-hold | continue dual-stopped PR. Standing phrases: until floor or loop | until greptile meets policy | pursue residuals until told otherwise. Skill pointer to § Operator follow-up after dual-stop / hard stop (#3273). Same affordance shape as swarm references/core-phase-4.md.
  • ⊗ Continue silent fix rounds after the envelope is exhausted.
  • ⊗ Reset the fix-batch counter solely by re-pushing, empty-committing, or swapping workers when the same primary finding fingerprint remains (poll-wait timer MAY reset for a new HEAD; the dual-stop fix-batch counter MUST NOT).

Enforcement note: skill defaults are behavioral. Durable delivery/acceptance circuit breaker: #3143 packages/core/src/delivery-attempt/ (evaluatePreDispatch). Docs: docs/delivery-attempt.md.

Operator follow-up after dual-stop / hard stop (#3273)

Operator-initiated resume after conf-hold, residual dual-stop, or hard-stop exit — not automatic re-thrash. Composes Greptile floor (#3095) and advisory should-not-merge (#3225). Portable consumer + maintainer (task / deft dual-invoke). Continue-until target is the resolved #3095 floor (plan.policy.review.minGreptileConfidence: typed project policy > framework dogfood 5 > consumer default 4; inspect task policy:show --field=minGreptileConfidence / deft policy:show --field=minGreptileConfidence). ⊗ Hard-code 5/5. ⊗ Lower project policy to clear one PR.

One-shot vs standing (#3448):

  • One-shot triggers: pursue residual · follow-up hard-stop · same as conf-hold · continue dual-stopped PR · re-babysit residual — one pass on the unit that just halted, then re-stop. Do not silently widen.
  • Standing triggers: until floor or loop · until greptile meets policy · pursue residuals until told otherwise — class A leftovers on every open unit in the active cohort / ordered plan keep moving until the resolved floor or the Same-fingerprint stop (this skill, Dual stop). Class B/C stay parked unless the operator names them.

One residual pass under operator consent (one-shot, or one standing batch):

  1. Ground-truth: dual-invoke pr:merge-ready / pr:watch --one-shot (#2893). Classify leftover A/B/C (#2881 table above).
  2. Apply one residual fix batch or one re-review wait — not both as an unbounded loop.
  3. If operator authorized a conf floor for this PR only (e.g. ≥4/5): post a PR audit comment (floor, HEAD SHA, authorizer). That is the human-merge / documented-override trail — it does not rewrite policy or make pr:merge-ready / pr:watch CLEAN below minGreptileConfidence. ⊗ Silent policy edit for one residual.
  4. Re-evaluate Step 6; merge when policy floor + gates met, or human-merge after the documented PR-local floor is met in the bot body; else halt again with a fresh resume line (leftover class + resolved floor + standing vs one-shot).
  5. Post-merge scope:complete when this owner holds lifecycle (#2321 / #3264).

! Same-fingerprint stop (normative, #3448 / #2442): 2 consecutive re-review observations, same primary fingerprint, no material fix between. Blocked handoffs count as observations. After a real fix, a new leftover MAY take another batch. #2442 batch cap (max 3 fix-batches) still applies.
⊗ Restate a competing 2-vs-3 same-fingerprint threshold, or treat a BLOCKED handoff as not counting toward the stop.

! Dual-stop re-entry: one residual pass then re-stop without new consent unless a standing order is active and the leftover is class A with a new fingerprint under the #2442 cap. Fresh operator consent required for another one-shot pass, or after same-fingerprint / cap halt.

⊗ Unlimited auto-retry after dual-stop without new operator consent (#3273 / #2442).
⊗ Treat conf-only holds as authorization for unbounded redesign (#2881).
⊗ Lower project-wide minGreptileConfidence for one residual.
⊗ Treat one-shot pursue residual as a standing order, or park a class A leftover on already-touched files because the score is below 5.

Step 3: Fix all findings in ONE batch commit

! Apply `coding/review.md` single-batch, cross-file grep, and local structured-data validation rules. Land all in-scope fixes in one commit (or one intentional batch), never per-finding pushes.

Greptile adapter pre-commit gate:

  • ! 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)

Step 3b: Proactive test coverage scan

! After committing the fix batch but before pushing, scan the changed lines for untested code paths:

  1. ! Run git --no-pager diff HEAD~1 HEAD --name-only to identify files touched in the fix batch
  2. ! For each changed file that has a corresponding test file, review whether the fix introduced or modified logic that lacks test coverage
  3. ! 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)
  4. ! 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.

! Mid-review push rule: see `coding/review.md` (no push while review in progress). Adapter rationale: every push re-triggers Greptile and resets the review clock.

⊗ Push any additional commits — including unrelated fixes, doc updates, or lessons — while waiting for the bot to finish reviewing the current head. 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 the dual-invoke probe order below over ad-hoc sleep loops (#1056 / #2893). Parse clean_gate_holdout on every probe.

Gates-surface dual invoke order (#2893 / #2878)

Deep-think gates (pr:watch, pr:merge-ready, review-monitor:*) have three ordered invoke paths. Agents MUST probe in this order; bare task pr:watch is not the sole (or first) consumer form.

! Probe order (MUST):

  1. deft / directive CLI first — attempt deft pr:watch --help (or directive pr:watch --help). Primary for npm/package-manager installs; works without a root Taskfile. CLI forms take flags without go-task's bare -- separator (e.g. deft pr:watch <N> --json, deft verify:review-monitor --pr <N>).
  2. task deft:<verb> second — when a root Taskfile includes .deft/core/Taskfile.yml under key deft:, go-task exposes namespaced tasks (task deft:pr:watch, task deft:verify:review-monitor, task deft:review-monitor:register). Probe task --list / task deft:pr:watch -- --help. Task forms require the bare -- separator before verb flags (e.g. task deft:pr:watch -- <N>, task deft:verify:review-monitor -- --pr <N>).
  3. #2878 gh-only fallback last — only when both CLI and namespaced task probes fail, classify missing-task: pr:watch (or missing-task: review-monitor) and use the official gh-only subset below.

⊗ Treat bare task pr:watch / task review-monitor:register as the only prescribed consumer form — under include key deft: those un-namespaced names are absent; that probe failure is not proof the gate is unavailable if deft or task deft: works (#2893).
⊗ Pass go-task's bare -- separator into deft/directive CLI forms (e.g. deft pr:watch -- --help) — CLI parsers reject the standalone -- and the probe fails falsely (#2893).

Missing gate surface / consumer gh-only fallback (#2878)

Some consumer repos (e.g. sister product deposits) ship Directive skills but have no working invoke path for pr:watch / review-monitor:* (no deft CLI on PATH, no Taskfile include → no task deft:…). Agents MUST NOT invent a non-skill poll loop when every probe fails.

! After dual-invoke probe fails (#2893): classify the session as missing-task: pr:watch (or the specific verb) and fail-loud:

BLOCKED: missing-task pr:watch on this consumer
Remediation:
  1. Prefer `deft pr:watch` / install `@deftai/directive` so the CLI surface works, OR
  2. Add the root Taskfile include (doctor gates-surface snippet) so `task deft:pr:watch` works, OR
  3. Use the official gh-only fallback subset below (still this skill — not freestyle).

! Official gh-only fallback (when both deft/directive and task deft:<verb> probes fail for pr:watch / review-monitor):

  1. Still select Approach 1 when a sub-agent primitive exists (OpenClaw sessions_spawn, Cursor Task, Claude Code Agent, spawn_subagent, start_agent) — spawn a review-monitor that runs the gh-only loop; do not block the parent.
  2. Do not call gate verbs via any surface the probe showed absent — those invocations cannot succeed and must not gate the spawn.
  3. Ownership claim without the tasks: post/update the sticky <!-- deft:review-owner --> PR comment via gh api (same field shape as the task-written lease) or keep ownership in the parent and document missing-task: review-monitor in the PR/parent handback. Never invent .deft/review-monitor.json.
  4. Poll with adaptive cadence (20-30s / 60s / 90s) using:
    • gh pr view <N> --comments (dual-source + Step 1 rules still apply)
    • gh pr checks <N>
    • gh api repos/<owner>/<repo>/pulls/<N> -q .head.sha for HEAD pin
    • gh api repos/<owner>/<repo>/commits/<sha>/check-runs for Greptile terminal check-run
  5. Evaluate the same Step 6 fail-closed all-of (terminal check-run + HEAD SHA + Last reviewed commit + confidence meets resolved min (policy/dogfood/default; see Step 6 #3095) + no P0/P1).
  6. Surface missing-task once to the operator/parent on first detection; do not silently rebrand freestyle sleep as pr:watch.

⊗ Fake a successful pr:watch or review-monitor:* gate when every dual-invoke probe failed.
⊗ Block Approach 1 / parent yield on missing review-monitor:* after the dual-invoke probe — use the gh lease claim or parent-owned gh-only poll instead (#2878).
⊗ Invent ad-hoc sleep / main-session poll / OpenClaw cron loops outside Approach 1–3 when the skill already names this fallback (#2878 / statusreport#153 recurrence).
⊗ Skip Step 6 fail-closed fields because deterministic tasks are missing — the gh surfaces above remain mandatory.

! When clean_gate_holdout=ci_failures and Greptile otherwise satisfies the probe-side Step 6 fields (SHA match on HEAD, confidence meets resolved min (policy/dogfood/default; see Step 6 #3095), 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.

CI weather reason codes + thrash caps (#3167)

pr:watch / pr:merge-ready expose machine-distinguishable ci_ready_state values (also --json) so agents stop conflating Actions weather with product test failure:

ci_ready_state / verdict Meaning Agent action
ci_never_scheduled / CI_NEVER_SCHEDULED No CI workflow check-run for HEAD (empty or bots-only: Greptile/SLizard) Cap re-triggers; then BLOCKED — do not multi-hour empty-commit thrash
runner_capacity_stall / RUNNER_CAPACITY_STALL Required still queued, no runner past budget (#2672) Wait auto-failover; ⊗ --skip-ci
ci_failures / CI_BLOCKED Completed failure / timed_out with product evidence (#2688) Own like Greptile P0 — fix or escalate
ci_cancelled_no_failover / CI_CANCELLED_NO_FAILOVER Primary cancelled and no green required sibling (failover skipped/not armed; workflow sibling #3168) Cap re-triggers; then BLOCKED
Greptile CLEAN fields / greptile_pending Existing Step 6 / SHA / confidence Unchanged

! Thrash caps (MUST): Under stable ci_never_scheduled, ci_cancelled_no_failover, or repeated capacity stall after failover wait budget, limit CI re-trigger attempts (empty-commit push, close/reopen, rebase-for-enqueue) to at most 2 total across the ownership span. On the 2nd failed re-trigger (or immediately when the weather code is stable and Greptile Step 6 is already clean), stop automatic re-push and emit a structured BLOCKED handoff.

Platform status probe + outage attribution (#3180)

! When ci_ready_state is weather-class (ci_never_scheduled, runner_capacity_stall, ci_cancelled_no_failover, ci_failures), CI never starts for HEAD, or many PRs share an empty-check pattern: MUST probe public status pages before workflow thrash or drive-by edits. Gates surface static URLs in pr:watch / pr:merge-ready JSON and human output (v1: no network fetch required).

  1. ! GitHub Status (Actions, Webhooks): https://www.githubstatus.com/
  2. ! Blacksmith Status: https://status.blacksmith.sh/

Attribution table:

Observation attribution Agent action
GH Actions and/or Webhooks major/partial outage platform Platform incident; ⊗ workflow drive-by edits; ⊗ empty-commit thrash past thrash caps; wait + re-check HEAD runs + local task check
Blacksmith red while GH Actions green capacity Runner-provider incident; failover doctrine (#2672 / #3168)
Both green + still ci_never_scheduled on this PR only repo_config Workflow paths, branch filters, required-check names, Actions disabled / org policy
Unclear / mixed unknown Cap thrash; BLOCKED with both status URLs; operator decision

! Anti-thrash during attributed platform outage: After thrash caps, stop automatic re-push loops. Remediation is wait + re-probe HEAD check-runs, not inventing workflow edits for a global outage.

Forge-outage drop-back (#3422)

! On attributed platform outage (attribution: platform) or repeated REST 429/502/503: drop back GitHub I/O (empty-commit, close/reopen, tight polls, new poller children), report once to the human in chat (what is down, attribution/incident, parked work, next probe time), and re-probe once per plan.policy.forgeOutageRetryMinutes (default 30; USER.md Personal wins; min 5; inspect task policy:show --field=forgeOutageRetryMinutes). Local work that does not need the forge MAY continue. Depth: `scm/github.md` § #3180 / #3422. Complements #3167 (weather codes) and #3180 (status attribution) — does not replace them.

⊗ Tight retry, empty-commit thrash, or sending the human to github.com as the only remediation.
⊗ Auto-merge / --skip-ci because a status page is red.

⊗ Merge or --skip-ci solely because a status page is red — status is attribution for wait/thrash policy, not a second branch-protection oracle.
⊗ Blame Blacksmith when GH Actions/Webhooks are the red components and Blacksmith runners are operational.
⊗ Edit workflows / empty-commit thrash to "fix" a documented global Actions/webhook outage without a status-page probe.

Cross-links: #3167 (weather codes), #3168 (failover arms), #2672 (capacity stall), #2688 (Greptile CLEAN + CI holdout).

! BLOCKED handoff template (CI weather):

BLOCKED: ci_weather
PR: <N>
HEAD: <sha>
REASON: ci_never_scheduled|ci_cancelled_no_failover|runner_capacity_stall
GREPTILE: CLEAN|P0/P1|pending (Step 6 fields)
CI: <ci_ready_state from pr:watch --json>
platform_status_github: https://www.githubstatus.com/
platform_status_blacksmith: https://status.blacksmith.sh/
incident_url: <optional status-page incident URL or n/a>
attribution: platform|capacity|repo_config|unknown
ATTEMPTS: empty-commit=<0-2> close-reopen=<0-2>
REMEDIATION: wait for platform recovery | capacity failover (#3168) | repo config fix | human admin-merge playbook below | operator decision
REDISPATCH_OK: yes|no

! Ownership of Greptile CLEAN + CI holdout is still real (#2688) — ownership ≠ infinite wait. After thrash caps, hand off; do not burn multi-hour gh run watch / empty-commit loops when Greptile is already CLEAN.

! Envelope selection under CI weather (#3153): Prefer deliberate stop-at: pr-open implement + this skill as the partner merge-path babysit when known capacity/outage weather dominates; see swarm Envelope selection SLA and partner merge-path section above. Cross-link only — do not re-spec the tree here.

⊗ Multi-hour empty-commit / close-reopen / rebase thrash after thrash caps when ci_ready_state is ci_never_scheduled or ci_cancelled_no_failover.
⊗ Treat ci_never_scheduled as ordinary Greptile latency (sha_match / terminal_check_run only).
⊗ Silent --skip-ci / admin merge without the outage playbook audit trail below.

SLizard advisory-only for merge-ready wait (#3167)

! Required bot for merge-ready wait / Step 6 exit ownership is Greptile. SLizard (Gemini Verify) is advisory only: timeouts, retries, missing check, or flaky SLizard conclusions MUST NOT gate the review-cycle wait loop or authorize unbounded re-push.

! task pr:merge-ready may still surface SLizard structure when present (#2189); agents MUST NOT idle-poll or thrash solely for SLizard when Greptile Step 6 is clean. Prefer continue / BLOCKED on CI weather / human decision.

⊗ Block merge-ready babysit on SLizard alone when Greptile Step 6 all-of is satisfied on HEAD.

Outage admin-merge playbook (opt-in / human, #3167)

When GitHub Actions is in a documented major outage (or multi-hour ci_never_scheduled / ci_cancelled_no_failover after thrash caps) and product + Greptile are ready:

! Admin / human merge is opt-in, never the autonomous agent default. Preconditions:

  1. ! Greptile Step 6 fail-closed all-of on current HEAD (dogfood conf floor / minGreptileConfidence).
  2. ! Evidence of local or prior green task check / merge-gate when available.
  3. ! PR comment audit note naming the weather code, HEAD SHA, and who authorized override.
  4. ? Optional explicit env/policy for bot merge (DEFT_ALLOW_BOT_MERGE / task policy:allow-bot-merge) — opt-in, never silent default skip-ci.

⊗ Autonomous --skip-ci / --admin merge as the default under ordinary CI weather.
⊗ Replace branch protection with Greptile-only merge on normal days.

Workflow failover arming (Blacksmith cancelled → GH-hosted lane) is sibling issue #3168 — this skill owns agent thrash caps and reason codes only.

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):

  1. Wait another N minutes (user picks N).
  2. Manually re-trigger Greptile by commenting @greptileai on the PR (logs the override in a PR comment for auditability per the next rule).
  3. Skip the bot review for this cycle and exit the loop with a documented reason.
  4. Cancel the review cycle entirely.
  5. Discuss.
  6. 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.

Owner Continuity Gate (#3090)

! After any claim of drive-to: merge-ready, babysit, shepherd, or "driving review-cycle / merge" on an open PR, the same turn MUST end in exactly one of:

  • A. Approach 1 monitor live + sticky <!-- deft:review-owner --> lease registered (or #2878 gh-only lease equivalent). Parent yields with an active child / monitor and emits review_cycle: in_progress:<pr>#<monitor_or_lease_ref>.
  • B. Parent-retained ownership: parent does not mark the work complete; the next concrete action is an explicit dual-source poll/fix (Step 1 + Step 6). Emit review_cycle: in_progress:<pr>#parent-retained (or lease id when a sticky lease exists).
  • C. Explicit finish to caller/principal: BLOCKED / needs decision / FAILED with PR URL + HEAD SHA + why. Emit review_cycle: skipped:<reason> or keep in_progress only when a live owner remains; never freeform started.

! review_cycle evidence enum (portable — all consumers / handoffs / preamble §11 / swarm finish messages): only

  • done — Step 6 fail-closed all-of met on current HEAD (terminal check + HEAD pin + confidence bar + 0 P0/P1 via dual-source)
  • in_progress:<pr>#<monitor_or_lease_ref> — verifiable sticky lease, registered monitor id, or documented parent-retained ownership
  • skipped:<reason> — intentional skip (e.g. no-pr, operator cancel)
  • n/a — work never entered review-cycle

! Layer L4 status: pass (or equivalent process-green handoff) is illegal unless review_cycle: done or review_cycle: in_progress:… with verifiable sticky lease / parent-retained ownership documented on that turn.

! Opening a PR may use in_progress + lease; the merge path requires done (stricter product merge bars remain out of scope).

! When dual-source fetch shows open P0/P1 under Step 6 / CLEAN evaluation: continue the fix loop or exit BLOCKED to the parent — never idle.

! Optional machine gate: deft verify:l4-owner --pr <N> (or task verify:l4-owner -- --pr <N>; dual-invoke same order as other gates). Exit 0 only when a sticky lease is fresh on the PR or the caller asserts --review-cycle done after Step 6 (lease-or-done machine gate). skipped / n/a / parent-retained are process evidence only and do not satisfy this machine gate. Exit 1 on silent hold (no lease, no done). Pair with existing monitor-without-lease regression (#2797).

⊗ End an owning turn with 0 children, no sticky lease, and no finish signal after a drive-to-merge / babysit / shepherd claim (silent hold).
⊗ Treat check-run SUCCESS alone as CLEAN or merge-ready while dual-source P0/P1 remain open.
⊗ Emit freeform review_cycle: started / pending / initiated or L4 status: pass without A (or parent-retained B with explicit next action) or full Step 6 done.
⊗ Solve Owner Continuity via host cron-as-Approach-1 or always-block-parent-until-merge — use A/B/C above (#2876 / #3090).

~ Eval / regression (#3090): Given PR open + check SUCCESS + open inline P1s + agent text claims driving merge + turn ends with 0 subagents and no lease → FAIL (Owner Continuity Gate), not PASS.

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) when the current agent is the top-level owner of that primitive (not a nested leaf — see Cursor leaf boundary). On Claude Code, top-level parents/orchestrators use the Agent tool with run_in_background: true (register primitive claude-agent, #3134); implementation leaves follow the Claude Code leaf boundary (blocking pr:watch or stop-at: pr-open sibling), not nested Agent. 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).

Partner merge-path when implement stops at PR-open (#3153)

! This skill is the required partner for the swarm Envelope selection SLA (skills/deft-directive-swarm/references/core-phase-0.md / Phase 3). When an implement leaf was deliberately scoped stop-at: pr-open (or a merge-ready leaf failed thin DONE and recovery chose babysit ownership), the parent/monitor MUST not leave the open PR without a merge-path owner.

! First-class merge-path ownership (MUST, same turn as PR ground-truth):

  1. ! Spawn or retain exactly one review-cycle owner for the open PR:
    • Approach 1 review-monitor (worker_role: review-monitor) with sticky <!-- deft:review-owner --> lease (#3090 / #3044 / dual-invoke review-monitor:register when available), or
    • A continuation leaf scoped drive-to: merge-ready on that PR/worktree that owns babysit → merge-ready in its tool loop, or
    • Documented parent-retained ownership (review_cycle: in_progress:<pr>#parent-retained) with an explicit next poll/fix action — never silent hold.
  2. ! Route through this skill — ⊗ Cursor global babysit (#2261), freestyle main-session poll, or dual parallel monitors (#3044).
  3. ! Apply Owner Continuity Gate (#3090) and Single review-monitor lease (#3044) without exception: one sticky lease; force-takeover only when the prior owner is dead.
  4. ! Post-merge scope:complete (#2321 / Gap C): When the implement leaf stopped at pr-open, it MUST NOT have run task scope:complete. After the PR merges, the merge-path owner (or swarm Phase 6 task swarm:finalize-cohort / task swarm:complete-cohort / monitor) MUST run task scope:complete or task scope:cancel. task verify:orphan-active fails closed on stranded active briefs.
  5. ! Human-merge / CLEAN-before-merge ownership (#3153 / #1193 / #2321): When plan.policy.requireHumanMerge (or missing bot-merge authority) means the review owner reaches Greptile CLEAN / merge-ready but cannot squash-merge, lifecycle ownership is not complete. Prefer durable ownership (not a prose-only promise):
    • Preferred: Parent/monitor retains ownership (review_cycle: in_progress:<pr>#parent-retained or sticky lease held by the long-lived monitor) until human merge lands, then runs scope:complete (or Phase 6 task swarm:finalize-cohort / task swarm:complete-cohort).
    • Allowed handoff: Only to a long-lived parent/monitor / Phase 6 closer — same turn: (1) re-claim sticky <!-- deft:review-owner --> lease for the recipient (register/force as needed), (2) structured handback with PR, HEAD, awaiting-human-merge, and explicit post-merge scope:complete duty, (3) recipient acknowledges with review_cycle: in_progress:<pr>#… before the giving agent exits.
    • ! Cohort through-merge: the swarm monitor is the default durable post-merge scope:complete owner for every stop-at: pr-open story (#2321) — do not rely on a review leaf that exits at CLEAN.
    • ! Post-CLEAN wake path (MUST): After CLEAN under human-merge, the durable owner MUST keep a reachable observe path until the PR is MERGED (or closed without merge → scope:cancel / operator decision). Concrete options (pick one, same ownership):
      1. Background poller (Approach 1 preferred when Tier 1): spawn/retain a review-monitor or short poll loop that probes gh api repos/<owner>/<repo>/pulls/<N> for merged / state on adaptive cadence (or host merge webhook when available), then runs post-merge verification + scope:complete.
      2. Parent-retained yield-with-wake: parent keeps review_cycle: in_progress:<pr>#parent-retained and re-enters on the next operator message / scheduled re-invocation (Approach 2) with an explicit first action of "check merge state → if merged, scope:complete".
      3. Phase 6 cohort closer: for swarm, record the PR as awaiting-human-merge in the monitor checkpoint and require the Phase 6 pre-sweep merge re-poll (skills/deft-directive-swarm/references/core-phase-5-6.md § Human-merge observe path / Step 1.5) before task swarm:complete-cohort / finalize — never skip scope:complete solely because Greptile was CLEAN earlier.
        ⊗ Keep only a sticky lease with no poller, no parent re-entry plan, and no Phase 6 re-poll checklist — that is ownership-in-name-only and still strands scope:complete.
        ⊗ Handoff to a short-lived leaf that exits at CLEAN without lease transfer.
        ⊗ Emit terminal DONE / stand down at CLEAN when merge authority is human-only and no reachable durable owner (sticky lease + live parent/monitor/cohort closer with an observe path above) remains for post-merge scope:complete.
  6. ! Thin DONE recovery (#2943 / #3153): A failed drive-to: merge-ready leaf that only opened a PR is not success. After ground truth, hand merge path to one of the owners above — never improvise a second lease or re-dispatch implement + babysit in parallel without releasing the first.

! Cohort through-merge intent is unchanged: stories still land on master. Envelope selection only assigns who owns implement vs who owns Greptile/CI/merge under capacity stall, conf floors, wall-clock budgets, or large multi-gate stories. Happy-path single drive-to: merge-ready leaves remain the default and do not use this partner handoff.

! Does not authorize: lowering minGreptileConfidence, --skip-ci for capacity stalls, or unbounded redesign on conf-only holds (#2881 / #2672 / #3095).

Cross-links: swarm decision tree skills/deft-directive-swarm/references/core-phase-0.md § Envelope selection SLA; Phase 3 Gap C skills/deft-directive-swarm/references/core-phase-3.md; thin SKILL pointer skills/deft-directive-swarm/SKILL.md § Envelope selection SLA.

! 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", per #1877 for Cursor as first-class Tier-1 tiers, per #2876 for OpenClaw sessions_spawn, and per #3134 for Claude Code claude-code / claude-agent). Probe the environment (tool set + env vars) to obtain the stable platform descriptor (grok-build, warp-orchestrated, warp-manual, cursor-composer, cursor-cloud-agent, claude-code, openclaw, etc.) from the launch adapter / get_platform_capabilities and map the descriptor to the appropriate tier + dispatch primitive (start_agent, spawn_subagent, the Cursor Task tool, Claude Code Agent / claude-agent, or OpenClaw sessions_spawn). 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, the Cursor Task tool with run_in_background: true, Claude Code Agent with run_in_background: true / register claude-agent, or OpenClaw sessions_spawn)
  • Tier 2 (no sub-agent primitive, but scheduler/timer/auto-reinvocation) → Approach 2 (yield-between-polls) — includes OpenClaw cron / host scheduler only when sessions_spawn is unavailable (#2876)
  • 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. If the descriptor is claude-code (Claude-unique Agent / CLAUDECODE / DEFT_PROBE_CLAUDE_CODE signals), treat as Tier 1 with the backgrounded Claude Code Agent poller path (#3134) — NOT Tier 3 and NOT cursor-composer (bare Task is not a Claude-unique signal). If the descriptor is openclaw (sessions_spawn present), treat as Tier 1 with the backgrounded sessions_spawn poller path (#2876) — NOT Approach 2 cron and NOT main-session gh poll.

! Swarm agents (whether launched via start_agent, spawn_subagent, or OpenClaw sessions_spawn 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 / #2876 / #2893): When Tier 1 is available and a dual-invoke probe succeeds for review-monitor verbs (deft verify:review-monitor / deft review-monitor:register, or task deft:verify:review-monitor / task deft:review-monitor:register), run that verify form before yielding, entering Approach 3, or claiming review monitoring started — CLI: deft verify:review-monitor --pr <N> [--call-site solo]; task: task deft:verify:review-monitor -- --pr <N> [--call-site solo]. After spawning Approach 1, claim the PR-anchored lease with the matching register form (CLI: deft review-monitor:register --pr <N> --monitor-agent-id <id> --platform-primitive ...; task: task deft:review-monitor:register -- --pr <N> --monitor-agent-id <id> --platform-primitive start_agent|spawn_subagent|cursor-task|claude-agent|sessions_spawn|openclaw-sessions-spawn). Release with the matching release form (CLI: deft review-monitor:release --pr <N>; task: task deft: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.

! Missing review-monitor surface carve-out (#2878 / #2893): When dual-invoke probes fail for pr:watch and review-monitor:* (no deft CLI, no task deft: include), do not invoke those gates and do not block Approach 1 on them. Fail-loud once with missing-task: review-monitor / missing-task: pr:watch, then:

  1. Still spawn Approach 1 with the official gh-only fallback when a sub-agent primitive exists.
  2. Post (or update) the sticky lease comment via raw gh api using the same <!-- deft:review-owner --> field shape the tasks would write — parent may yield after that claim succeeds — or, if the agent cannot write issue comments, keep ownership in the parent with the gh-only poll and document that lease tasks were unavailable.
  3. Never invent a local .deft/review-monitor.json as a substitute gate.
    ⊗ Require successful review-monitor register/verify on a consumer where dual-invoke probes failed — that is the conf=3 / #2878 inconsistency Greptile flagged.

! Regression trigger (#2797 / #2878): A leaf that claims a monitor is active without a preceding successful GitHub sticky lease claim MUST fail the review-monitor checklist/eval; a backgrounded pr:watch shell is insufficient. When a dual-invoke probe finds review-monitor:register, that form is the required claim path. When the missing-task carve-out applies (#2878), a raw gh api post/update of the same <!-- deft:review-owner --> sticky comment satisfies the claim (or the parent keeps ownership and does not claim a separate monitor). The regression fails only on a claim with no sticky lease evidence — not on consumers that legitimately lack the gate surface.

! 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 / #2893): 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 deft pr:watch <N> (or task deft:pr:watch -- <N> when include-present), or its envelope MUST instead be stop-at: pr-open so the orchestrator can spawn and register a sibling review-monitor. A background shell pr:watch is not a monitor and MUST NOT be claimed as one.

! Claude Code leaf boundary (#3134 / #2797 analogue): Nested Agent (implementation leaf spawning a second-level review-monitor Agent) is unsupported/unreliable on some Claude Code surfaces. A Claude Code drive-to: merge-ready leaf MUST either keep ownership in the same agent by blocking on deft pr:watch <N> (or task deft:pr:watch -- <N> when include-present), or its envelope MUST instead be stop-at: pr-open so the orchestrator can spawn and register a sibling review-monitor with --platform-primitive claude-agent. ⊗ Background a nested Agent poller from an implementation leaf and exit claiming monitoring is active. Top-level parents/orchestrators that own the Agent primitive MAY still Approach-1 background a review-monitor Agent.

Approach 1 (preferred -- sub-agent orchestration available per platform descriptor):

! Background dispatch (#1880 / #2876 / #3134): Spawn the review-monitor sub-agent via the matching primitive IN THE BACKGROUND (Cursor: Task run_in_background: true; Claude Code: Agent run_in_background: true; Grok Build: spawn_subagent with parent yielding; OpenClaw: sessions_spawn with parent yielding). The parent MUST remain interactive while the poller runs — never block the parent OpenClaw/Cursor/Claude Code/Grok session for >~3 min of monitor ownership.

! Heartbeat contract for Cursor pollers (#1877 / #1166 / #2876 / #3134): Claude Code Agent and OpenClaw sessions_spawn pollers share this contract. A Cursor Task, Claude Code Agent, or OpenClaw sessions_spawn 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.

~ Visible Control UI (OpenClaw / #3044): When OpenClaw Control UI is the operator control plane, SHOULD spawn the review-monitor with visible:true when the tool surface allows so humans can inspect progress without attaching to the parent session; invisible empty settles are higher FC04 residual risk.

! When the platform descriptor indicates Tier 1 (sub-agent support), spawn a review-monitor sub-agent using the primitive matching the descriptor:

  1. ! Launch via the matching primitive: start_agent (Warp), spawn_subagent (grok-build / TUI / non-Warp), the Cursor Task tool with run_in_background: true (cursor-composer / cursor-cloud-agent, #1877), the Claude Code Agent tool with run_in_background: true (claude-code / register claude-agent, #3134), or OpenClaw sessions_spawn (openclaw, #2876) with a prompt that instructs it to poll for Greptile completion. For spawn_subagent, Cursor Task, Claude Code Agent, and OpenClaw sessions_spawn 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).
  2. ! 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; for Claude Code Agent the backgrounded-agent completion-notification path; for OpenClaw sessions_spawn the host session completion / messaging channel (prefer task pr:watch inside the child when available). Front-load the first check to catch fast reviews.
  3. ! When the exit condition is met (Greptile review current on the HEAD commit SHA, confidence meets resolved min (policy/dogfood/default; see Step 6 #3095), no P0/P1 remaining), the sub-agent reports completion back to the parent (via send_message_to_agent, the spawn_subagent result channel, or the OpenClaw sessions completion channel).
  4. ! The main conversation pane stays fully interactive during monitoring -- the user (or parent monitor) can continue other work.
  5. ! 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.

⊗ Use OpenClaw cron alone as Approach 1 when sessions_spawn is available — cron is Approach 2 scheduler fallback only (#2876).

Empty announce ≠ done (parent DoD) (#3044 / FC04 residual)

! When a review-monitor settle arrives with empty body, missing STATUS: line, or status unknown (including host (no output) / empty subagent_announce):

  1. ! The parent MUST run same-turn ground truth before any DONE / CLEAN / merge-ready claim: at least gh pr view <N> (or REST pulls/<N>), gh pr checks <N>, and current HEAD SHA (gh api repos/<owner>/<repo>/pulls/<N> -q .head.sha).
  2. ! Classify the settle as FC04 residual (empty babysit ≠ done) until ground truth shows a terminal merge/close outcome or an explicit structured BLOCKED / FAILED handback.
  3. ⊗ Treat empty / unknown settle as DONE, CLEAN, merge-ready, or batch-complete.
  4. ⊗ Spawn a second review-monitor solely because the first settle was empty/unknown without completing the ground-truth batch first (#3044 dual-lease recurrence).

~ Recurrence: enterprize PR #43 (2026-08-02) — first monitor polled live, host settled empty/unknown; parent spawned a second same-taskName monitor; dual lease collision while PR stayed open. See also meta/lessons.md and FC04 / growth friction R1 + R10.

Completion latch on settle replay (#3092)

! Empty settle is not a latch hit — use Empty announce ≠ done (#3044) above. When the parent has already emitted one consolidate for a monitor/leaf runId and the harness re-delivers an identical or equivalent non-empty settle for the same key, stay silent (no re-QC, no second final; host silent token when defined). Re-open only on new runId, principal reopen, or material new evidence. Full MUST: templates/agent-prompt-preamble.md §11.5.

Single review-monitor lease (#3044 / #2814)

! One sticky lease per PR: ownership is the single sticky GitHub PR comment <!-- deft:review-owner --> (or the dual-invoke review-monitor:register form that writes it). Parallel ownership is forbidden.

! Pre-spawn check: before launching another Approach 1 review-monitor (sessions_spawn, spawn_subagent, Cursor Task, Claude Code Agent, start_agent):

  1. ! Read the sticky lease (dual-invoke verify:review-monitor when available, else gh api issues comments for <!-- deft:review-owner -->).
  2. ! List active same-PR / same-taskName subagents when the host exposes that surface (OpenClaw subagents list or equivalent).
  3. ⊗ Spawn a second monitor while a prior owner is running.
  4. ⊗ Spawn a second monitor when the last settle was empty/unknown and ground truth has not shown a terminal merge/close (or explicit structured handback that releases ownership).
  5. ! If the prior owner is dead (liveness fail / REDISPATCH_OK / verify:subagent-alive exit 1) and the PR is still open: spawn one replacement monitor and re-claim the lease with --force (CLI: deft review-monitor:register --pr <N> --monitor-agent-id <id> --force / task: task review-monitor:register -- --pr <N> --monitor-agent-id <id> --force, or host equivalent force takeover) so a non-expired foreign lease does not block replacement — then update the sticky lease comment to the new owner. Never silent dual ownership.
  6. ! On register conflict when the prior owner is still alive: attach to the existing owner or stop — do not parallel-fix.
  7. ⊗ Refuse replacement of a dead owner solely because the 30-minute lease has not expired without attempting force takeover (#3044).

Advisory pass-open mark on issue threads (#3607)

! Advisory, not a lock. On an issue thread the same <!-- deft:review-owner --> marker carries kind: pass to say a structured pass (triage, design-critique, review-response) is open: pass kind, owner, declared ceiling, and expires_at. An arriving agent reads and is informed; nothing is held and no write is blocked. A pass has N+1 writers by construction (parent plus panel), so an exclusive hold names no actual actor — blocking was refuted 3/3 by the #3607 panel.

! Read before writing into an open pass. Fetch the mark (fetchActivePassMarker, or gh api repos/<owner>/<repo>/issues/<N>/comments filtered on the marker). When a mark is open, prefer flagging your comment as post-ceiling, or hold voluntarily — your call, not a gate.

! The lifecycle runs on the issue thread itself — there is no separate verb. Open the mark by posting a comment whose body is the marker block; clear it at synthesis by editing that same comment to carry ended_at. Engine callers use openPassMarker / closePassMarker / fetchActivePassMarker (packages/core/src/review-monitor/github-lease.ts); an agent without those bindings posts the identical block through the safe-body verbs, exactly as the #2878 gh-only lease fallback does.

<!-- deft:review-owner -->
kind: pass
pass_kind: design-critique
owner: <github-login>
agent_id: <pass owner agent id>
ceiling: <declared ceiling comment id>
started_at: 2026-08-28T19:48:24Z
expires_at: 2026-08-28T20:48:24Z
<!-- /deft:review-owner -->
  1. ! Open at pass start with a new comment, and keep the comment id it returns: task scm:body:comment:create -- --repo <owner>/<repo> --issue <N> --body-file <file>.
  2. ! Read on arrival: gh api repos/<owner>/<repo>/issues/<N>/comments, then take the oldest unexpired kind: pass block. A mark already open means you were informed, not stopped.
  3. ! Refresh or clear only the comment id your own open returned: task scm:body:comment:edit -- --repo <owner>/<repo> --comment <id> --body-file <file>, adding ended_at: <now> at synthesis.

⊗ Edit a marker comment you did not open, including one carrying your own login from another pass — a comment belongs to the pass that created it. Editing another author's comment is also a 403 for a non-maintainer. Open your own and let oldest-comment-id-wins settle which mark arrivals honour.

! Expiry is the release. A mark self-clears on read once expires_at passes, and the owner clears it at synthesis (ended_at), so an abandoned pass never marks a thread forever and no heartbeat is needed. Concurrent marks resolve oldest comment id wins, matching the lease; the later mark is removed and its author is told which mark stands.

! Trust boundary. Pass marks are read from any author association, including CONTRIBUTOR, because they inform rather than gate. Ownership leases stay maintainer-authored (OWNER / MEMBER / COLLABORATOR, #2307) because verify:review-monitor / verify:l4-owner exit 0 on a live lease.

⊗ Treat a pass mark as permission to hold, block, or gate another actor's write.
⊗ Read a kind: pass mark as a review-owner lease — a pass mark never satisfies verify:review-monitor / verify:l4-owner.

Required non-empty monitor handback (#3044)

! Approach 1 review-monitor prompts (including templates/swarm-greptile-poller-prompt.md and any host-filled spawn prompt) MUST require a non-empty final handback with these fields:

STATUS: DONE|BLOCKED|FAILED
HEAD: <sha>
CHECKS: <summary>
MERGE: <url|error|n/a>
ISSUE: <closed|open|n/a>
NOTES: <short>

⊗ Empty final assistant message from a review-monitor.
⊗ Parent treating a settle that lacks STATUS: as success.

~ Visible Control UI risk (#3044): When OpenClaw Control UI is the operator plane, prefer visible:true on the review-monitor spawn; invisible empty settles are higher risk for FC04 misclassification. Cross-link: skills/deft-directive-swarm/references/host-openclaw.md Babysit / review-monitor residual.

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.

  1. ! 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>.
  2. ! After each check, yield control (end all tool calls) -- the agent runtime will re-invoke after its interval or on next interaction.
  3. ! 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.
  4. ! No blocking shell pane lock -- the conversation remains interactive between checks.
  5. ~ 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.
  6. ! 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.

  1. ! 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
  2. ! Poll using gh pr view <number> --comments and gh pr checks <number> in the same shell session
  3. ! When the exit condition is met (Greptile review current, confidence meets resolved min (policy/dogfood/default; see Step 6 #3095), no P0/P1), exit the loop and proceed to Step 5
  4. ! 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:

  1. 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
  2. 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).

  1. ! 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.

  2. ! 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.

  3. ! 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.

  4. ! Confidence meets resolved min (#3095) — the parsed Confidence Score: X/5 is >= the resolved floor from plan.policy.review.minGreptileConfidence (inspect: task policy:show --field=minGreptileConfidence; pr:watch / pr:merge-ready share this SoT). Resolution order: typed project policy > framework dogfood detect (framework source → 5) > consumer default (4, legacy confidence > 3 / 4/5+). A score below the floor, an unparsed confidence, or an absent confidence is unknown. Directive dogfood MUST NOT exit CLEAN on 4/5.

  5. ! 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.

Mechanical mergeability is necessary, never sufficient (#3225)

! GitHub Ready to merge, green required checks, and formal review state without Changes-Requested are necessary but never sufficient clean signals. Reviewer bots on repos without enforced review wiring often express should-not-merge + sub-threshold confidence only in comment body prose (no formal REQUEST_CHANGES, no blocking CheckRun). A shepherd that merges on mechanical signals alone merges against explicit advisory verdicts.

! task pr:merge-ready / task pr:watch / Step 6 clean MUST parse advisory bot verdict text and confidence from comment bodies (the #1282-style body extraction path; shared detector in packages/core/src/content-contracts/skills/greptile-detector.ts). Treat any of the following as blocking regardless of formal review state or the Ready-to-merge box:

  1. Advisory should-not-merge prose (should-not-merge, Not safe to merge, Do not merge, Safe to merge once corrected, not ready to merge, …)
  2. Confidence below the resolved minGreptileConfidence floor (#3095 — project policy > dogfood 5 > consumer default 4)
  3. P0/P1 findings (triple-tier + inline threads) or errored Greptile sentinel

! When advisory prose blocks or confidence is sub-threshold: continue the fix/document loop or exit BLOCKED — ⊗ do not treat mechanical mergeability as CLEAN.

~ Recommend repo-level review-gate wiring (required review / blocking Greptile CheckRun) for org repos that lack it, so formal state and prose verdict cannot diverge silently. Directive dogfood already enforces #3095; portable babysit paths on other org repos still need this prose gate.

⊗ Merge because the merge box says Ready to merge while bot comment prose says should-not-merge or confidence is below the resolved floor (#3225 / #3095).
⊗ Treat formal Comment (non-blocking) review state as sufficient when the rolling-summary body carries an advisory block.

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.

! Re-poll MUST also re-check advisory should-not-merge prose + confidence (#3225) — mechanical Ready-to-merge alone is never sufficient.

⊗ 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).
⊗ Call gh pr merge on mechanical Ready-to-merge / green checks while advisory bot prose still records should-not-merge or sub-threshold confidence (#3225).

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:

  1. Comment @greptileai review on the PR to retrigger a canonical rolling summary on the current HEAD.
  2. Wait for Greptile to edit its primary rolling-summary comment with both canonical fields, then re-run task pr:merge-ready -- <N>.
  3. 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

! Apply `coding/review.md` post-merge closing-keyword verification. Adapter HOW for GitHub:

! 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).

  1. ! For each issue referenced with a closing keyword in the PR body, run:
    gh issue view <N> --json state --jq .state
  2. ! 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)"
  3. ~ This step mirrors skills/deft-directive-swarm/SKILL.md Phase 6 Step 2 and applies to ALL PR merges, not just swarm runs.
  4. ! 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).

Probe-then-fill remote claims (#3120)

! Before filling any remote handoff field (PR URL, PR number, commit/HEAD SHA, CI green/success, review score) or claiming status: pass / ship/gate done, MUST probe then fill:

  1. Run same-turn git + forge probes (examples: git rev-parse HEAD, gh api repos/<owner>/<repo>/pulls/<N>, task pr:watch -- <N> --one-shot, checks API).
  2. Copy IDs / URLs / SHAs / scores only from that probe JSON/text into the evidence block.
  3. Set proof_status: bound and attach short raw probe snippets (command + snippet) for each remote claim.

! Handoff evidence axes: work (local) / ship (pushed branch or PR) / gate (CI/review on HEAD). proof_status is bound | unbound | n/a-no-remote-claim.
! Legal partial: local work done + ship not_started / blocked without PR/SHA/CI/review fields and proof_status: n/a-no-remote-claim (or status: partial) is valid — do not invent ship state.
! Fail ranking: invented-done (false/unbound remote artifacts under pass) is stricter than empty-done. Unbound remote claims → invalid evidence (fail), not pass-with-notes.
! Machine check: validateHandoffEvidence in packages/core/src/handoff-evidence/ (see templates/agent-prompt-preamble.md §11).
⊗ Fill PR / SHA / CI / review fields from recollection, narration, or prior-turn memory.
⊗ Claim status: pass with remote fields when proof_status is not bound or probes are missing (#3120).

Anti-Patterns

  • ⊗ Multi-hour empty-commit / close-reopen thrash after CI weather thrash caps when ci_never_scheduled or ci_cancelled_no_failover (#3167)
  • ⊗ Workflow thrash or empty-commit spam during attributed platform outage without status-page probe (#3180)
  • ⊗ Tight forge-outage retry / empty-commit thrash without a one-shot human report (#3422)
  • ⊗ Merge or --skip-ci solely because a status page is red without check-run evidence (#3180)
  • ⊗ Block merge-ready wait on SLizard alone when Greptile Step 6 is clean (#3167)
  • ⊗ Silent admin / --skip-ci merge under Actions outage without audit comment and opt-in authority (#3167)
  • ⊗ Leave a deliberate stop-at: pr-open (or thin-DONE recovery) open PR without spawning/retaining one review-cycle babysit owner + lease continuity and post-merge scope:complete plan (#3153)
  • ⊗ Stand down at CLEAN under human-merge policy without a durable owner (sticky lease + live parent/monitor/Phase 6 closer) and a post-CLEAN observe path (poller / parent wake / Phase 6 re-poll) for post-merge scope:complete (#3153 / #1193 / #2321)
  • ⊗ Handoff human-merge cleanup to a short-lived leaf that exits at CLEAN without re-claiming the sticky lease (#3153)
  • ⊗ Retain only a sticky lease after CLEAN with no poller, no parent re-entry, and no Phase 6 merge re-poll — ownership-in-name-only (#3153)
  • ⊗ Dual-lease or freestyle Cursor global babysit for the partner merge-path after implement stops at PR-open (#3153 / #2261 / #3044)
  • ⊗ End owning turn with 0 children, no sticky lease, and no finish after drive-to-merge / babysit / shepherd claim — silent hold (#3090)
  • ⊗ Emit freeform review_cycle: started / pending / initiated or L4 status: pass without done or verifiable in_progress:<pr>#… lease/parent-retained (#3090)
  • ⊗ Treat check-run SUCCESS alone as CLEAN / merge-ready while dual-source P0/P1 remain (#3090)
  • ⊗ Ignore `coding/review.md` while running this adapter — universal batch/severity/exit/post-merge rules live there
  • ⊗ Route PR shepherding to Cursor global babysit on Deft-managed repos when .deft/core/ is installed -- use this review-cycle skill instead (#2261)
  • ⊗ Route OpenClaw babysit/shepherd/watch to main-session gh poll + cron when sessions_spawn is available -- use Approach 1 with sessions_spawn (#2876 / #2261)
  • ⊗ Treat OpenClaw cron alone as Approach 1 — cron/timer is Approach 2 only if spawn is unavailable (#2876)
  • ⊗ Route Claude Code babysit/shepherd/watch to Approach 3 blocking poll when Claude Agent spawn is available — use Approach 1 with backgrounded Agent / claude-agent (#3134)
  • ⊗ Misclassify Claude Code as cursor-composer from bare Task alone (#3134)
  • ⊗ Expand active story scope past xBRIEF AC mid-babysit without follow-up issue or consented brief amend (#2881)
  • ⊗ Treat confidence-only holds (0 P0/P1) as a mandate for unbounded redesign (#2881)
  • ⊗ Dual-stop / conf-residual terminal halt without #3273 resume line (leftover class + residual class + resolved floor + standing vs one-shot + pursue residual / until floor or loop + skill section pointer) (#3273 / #3448)
  • ⊗ Unlimited residual auto-retry after dual-stop without new operator consent (#3273 / #2442)
  • ⊗ Hard-code 5/5 as the continue-until target, or treat one-shot pursue residual as standing, or park a class A already-touched leftover as a new story (#3448 / #2881 / #3095)
  • ⊗ Invent freestyle sleep/poll loops when dual-invoke probes fail for pr:watch — use the official gh-only fallback and fail-loud missing-task (#2878 / #2893)
  • ⊗ Treat bare task pr:watch as the only consumer gate form — probe deft then task deft: first (#2893)
  • ⊗ 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)
  • ⊗ Merge on mechanical Ready-to-merge / green checks while bot comment prose records should-not-merge or confidence below minGreptileConfidence (#3225 / #3095)
  • ⊗ Treat formal non-blocking review state as sufficient when rolling-summary body carries an advisory block (#3225)
  • ⊗ Treat empty/unknown review-monitor settle as DONE/CLEAN/merge-ready without same-turn ground truth (#3044 / FC04 residual)
  • ⊗ Spawn a second review-monitor while prior owner is running or last settle was empty/unknown without terminal ground truth (#3044)
  • ⊗ Accept empty review-monitor final message missing STATUS/HEAD/CHECKS/MERGE handback (#3044)
  • ⊗ Invent remote PR/SHA/CI/review claims in handoff evidence without same-turn probe binding — invented-done (#3120)
  • ⊗ Fill remote ship/gate fields from memory when only local work completed; legal partial omits PR fields (#3120)