Imported from harshanandak/forge (
skills/shepherd/SKILL.md). Install upstream withnpx skills add harshanandak/forge --skill shepherd. Copyright stays with the author.
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 daemonis 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)
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_READYand 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-rebaserebases 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_HEADrecovery, which needs the maintainerFORGE_PR_TOKEN). - Required-check gate.
MERGE_READYonly 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
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.