# Shepherd

> Own open PRs to merge-readiness — autonomously. Forge provides a singleton shepherd daemon — start it with `forge shepherd daemon` — that then watches every open PR, converges CI check state into kernel verdicts, re-runs flaky required checks, reaps orphan watchers, and self-retires when no PRs remain; one-shot passes (forge shepherd <pr>) exist for a single bounded check. Use when a PR was just opened or shipped; when a session starts with open PRs (Forge automatically wakes the singleton; the lease makes a duplicate start a safe no-op); when the user asks "why isn't my PR merging", "what's blocking the PR", "is the PR ready", "did a check fail / go red"; when asked to "keep watching / keep an eye on / babysit my PRs"; or to read PR verdicts (forge shepherd <pr> --pull --json, forge shepherd events). NEVER merges and never resolves review threads — fixing review feedback is `review`; opening the PR is `ship`; post-merge health is `verify`; general orientation is `status`.

- Skill: `harshanandak/shepherd` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add harshanandak/shepherd`
- Raw SKILL.md: https://api.skillmd.com/api/skills/harshanandak/shepherd/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: harshanandak (https://skillmd.com/u/harshanandak)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/harshanandak/shepherd

---


Forge owns your open PRs. A singleton daemon converges every open PR toward merge-readiness; you read its verdicts and only step in on escalations. Never merges, never resolves review threads.

# Shepherd

`shepherd` is the PR-ownership utility, not a workflow stage. It has two modes over the **single `forge shepherd` verb**:

- **Daemon (default ownership):** `forge shepherd daemon` is a machine-wide singleton for this repo. It heartbeats and converges the whole PR world every ~60s — self-registering hand-opened PRs, restarting killed watchers, reaping verified orphans, converging CI check state into kernel verdicts, retiring merged/closed PRs — then self-retires (releases the lease, kills its children, exits) once no PRs remain open. You do NOT poll by hand; the daemon does.
- **One-shot convergence:** `forge shepherd <pr>` runs the local review preflight, reads one PR's current-head state, takes at most one idempotent Tier-A action, persists bounded deltas/receipts, and exits. Use it for a point-in-time question or one bounded convergence attempt on a single PR.

## Which mode? (decision rule)

- User asks about **one PR, right now** ("is #212 ready?", "why is this blocked?") → **one-shot** (`forge shepherd <pr> --pull`).
- Anything **ongoing** — "keep", "until it merges", "watch", "babysit", multiple PRs, or a session that starts with PRs already open → **ensure the daemon is running**.

## Automatic attachment

Forge wakes the singleton after a successful supported session start, every
successful push, and every successful non-dry-run ship. Push does not require a
locally resolved PR number; the daemon enumerates all open and draft PRs itself.
Ordinary commands do not launch it.

The daemon is a **repo singleton** guarded by an O_EXCL lease, so no liveness
check is needed first. Forge prefers a supplied harness background shell; its
bare-CLI detached fallback launches from the stable common repository root, not
the current disposable worktree. Agents never launch a detached spawn directly.
(A `forge prime` daemon-liveness line remains the separate W-S5 follow-up.)

## Reading verdicts (the common case)

```bash
forge shepherd <pr> --pull --json          # actionable payload: WHY blocked + exactly what to fix
forge shepherd <pr> --bundle --json        # the COMPLETE read-only PR-state bundle
forge shepherd events <pr> --since <seq>   # only the new events since sequence <seq>
```

`--pull` is strictly read-only (dry-run pass: no rerun, no rebase, no merge, no thread resolution). It returns one bounded, actionable-only payload — `blockers[]`, classified `requiredChecks`, failed-check log `failures[]` (matrix-deduped), and every unresolved `reviewThreads[]` — so you get "everything blocking this PR + what to fix" in one call. Passing checks and satisfied policy are omitted.

A plain `forge shepherd <pr>` also returns a consolidated `localPreflight`, at
most 128 `deltas`, `receiptIds`, and one explicit `handoff`. The preflight probes
CodeRabbit and runs strict lint, Sonar parity, structural drift, and affected
tests. An unavailable or unauthenticated CodeRabbit CLI is reported as
`UNAVAILABLE`/`INCOMPLETE`, never as a fabricated pass; deterministic failures or
actual review findings make the remote decision read-only for that pass. A
checkout that is not the exact PR head is `NOT_APPLICABLE`, never reviewed as if
it were authoritative.

### Verdict vocabulary (collapsed, W-S1)

| Verdict | Meaning |
| --- | --- |
| `MERGE_READY` | Required checks green, branch up to date — hand off to a human to merge. |
| `PENDING` | A Tier-A action was taken, or checks are still running — await the next tick/pass. |
| `INCOMPLETE` | Exact-head local review or durable convergence evidence is unavailable — fail closed and retry after evidence is restored. |
| `BLOCKED` | Something actionable blocks merge (failing/missing/skipped required check, conflict, behind, unresolved threads, changes requested). Read `blockers[]`. |
| `CI_DEAD_HEAD` | The head has no required checks running (e.g. an auto-update authored by `GITHUB_TOKEN` never re-triggered CI). Recovery is an **escalation, not an autonomous Tier-A rerun**: it needs a maintainer-provided `FORGE_PR_TOKEN` (contents + pull-requests + checks) to re-author the push so CI re-triggers. |
| `ESCALATE` | A Tier-C condition (conflict, unreadable required set, persistent failure, oscillation, budget exhaustion). Context is posted to the PR. |
| `HARD_STOP` | A permanent auth/scope failure retrying cannot fix — a human must widen token scope. |

## Trigger scenario → command

| Situation | Command |
| --- | --- |
| PR just opened / shipped | automatic singleton wake; read with `forge shepherd <pr> --pull` |
| Session starts with open PRs | automatic singleton wake |
| "Why isn't my PR merging / what's blocking it" | `forge shepherd <pr> --pull` |
| "Is the PR ready?" | `forge shepherd <pr> --pull` (read `MERGE_READY`) |
| "A check failed / went red" | `forge shepherd <pr> --pull --json` (read `failures[]`) |
| "Keep watching / babysit my PRs" | ensure `forge shepherd daemon` running |
| Read incremental deltas | `forge shepherd events <pr> --since <seq>` |

## Boundaries (kept — true of both modes)

- **Never merges.** No merge action, no server-side auto-merge latch. Terminates at `MERGE_READY` and hands off — a human merges in the GitHub UI.
- **Never resolves review threads.** It refreshes a single **sticky** status comment; thread *resolution* is semantic and stays with `review`.
- **Action ladder.** Tier-A (autonomous, idempotent): re-run a flaky **required** check (rerun-budget capped); refresh the single **sticky** status comment — an *upsert*, never an append, so the ~60s daemon loop cannot post duplicate comments. Tier-B (opt-in, default OFF): `--auto-rebase` rebases onto base and force-pushes with lease — a lease rejection is a hard-stop, never re-armed. Tier-C: everything else escalates (incl. `CI_DEAD_HEAD` recovery, which needs the maintainer `FORGE_PR_TOKEN`).
- **Required-check gate.** `MERGE_READY` only when the branch-protection required set is *known* and all green; if protection is unreadable, it escalates rather than guessing.
- **HEAD-changed abort.** Before any mutating action it re-reads the head SHA and aborts if HEAD moved.

## Adjacent skills

- Fixing review feedback (CodeRabbit/Greptile/human comments, resolving threads) → `review`.
- Opening or pushing the PR → `ship`.
- Post-merge health (CI green on main, close issues) → `verify`.
- "Where am I / what's in flight" orientation → `status`.

## Kill-switches

```bash
FORGE_SHEPHERD_DISABLE=1              # env: makes automatic firing inert
forge gate disable rail.auto_shepherd # config gate honored by every automatic seam
```

CI/test (`NODE_ENV=test`, `BUN_ENV=test`, `CI`, `GITHUB_ACTIONS`, or
`GITLAB_CI`), dry-run, uninitialized repositories, and disabled paths return
before lease, Kernel-state, or process work. All leave the manual
`forge shepherd` surface usable; they only stop automatic daemon fire.

## State

Public Memory is the durable monitor authority and public Flow reducers restore
its bounded event and watcher-process checkpoints after restart. The per-PR
journal under `.forge/pr-monitor/<repo>-<pr>/` remains a compatibility delivery
surface for `events --since`, not authority. Merged/closed evidence produces one
idempotent terminal MonitorReceipt; conflicting or incomplete replay fails
closed. For 0.1 no receipt grants continuing lease authority: live ownership is
re-probed at consequential boundaries, and canonical LeaseReceipt epoch/scope
plus the same-actor/session ABA fix remain explicitly deferred.

