pr-shepherd 0.52.0 → 0.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +11 -8
  3. package/bin/cli/help-command-pages.d.mts +1 -1
  4. package/bin/cli/help-iterate-poll-pages.d.mts +1 -1
  5. package/bin/cli/help-iterate-poll-pages.mjs +4 -3
  6. package/bin/cli/help-top-page.d.mts +1 -1
  7. package/bin/cli/help-top-page.mjs +3 -2
  8. package/bin/cli/help.d.mts +2 -2
  9. package/bin/cli/iterate-instructions.mjs +41 -0
  10. package/bin/cli/iterate-lean.mjs +1 -0
  11. package/bin/cli/poll-summary-formatter.mjs +3 -1
  12. package/bin/cli/runner.d.mts +1 -0
  13. package/bin/cli/runner.mjs +3 -1
  14. package/bin/commands/check.mjs +13 -4
  15. package/bin/commands/clean.mjs +7 -13
  16. package/bin/commands/iterate/check-instructions.d.mts +2 -2
  17. package/bin/commands/iterate/check-instructions.mjs +11 -7
  18. package/bin/commands/iterate/escalate.mjs +4 -0
  19. package/bin/commands/iterate/fix-code.mjs +6 -1
  20. package/bin/commands/iterate/helpers.d.mts +0 -1
  21. package/bin/commands/iterate/helpers.mjs +0 -15
  22. package/bin/commands/iterate/index.mjs +57 -27
  23. package/bin/commands/iterate/merge-state.mjs +6 -4
  24. package/bin/commands/iterate/native-stack-rebase.d.mts +34 -0
  25. package/bin/commands/iterate/native-stack-rebase.mjs +43 -0
  26. package/bin/commands/iterate/parent-first.d.mts +18 -2
  27. package/bin/commands/iterate/parent-first.mjs +45 -32
  28. package/bin/commands/iterate/render.d.mts +1 -1
  29. package/bin/commands/iterate/render.mjs +7 -12
  30. package/bin/commands/iterate/stale-ancestry.d.mts +1 -1
  31. package/bin/commands/iterate/stale-ancestry.mjs +10 -7
  32. package/bin/commands/iterate/stall.mjs +40 -3
  33. package/bin/commands/poll-progress.d.mts +5 -1
  34. package/bin/commands/poll-progress.mjs +7 -1
  35. package/bin/commands/poll-summary-instructions.d.mts +6 -1
  36. package/bin/commands/poll-summary-instructions.mjs +51 -87
  37. package/bin/commands/poll-summary.mjs +4 -2
  38. package/bin/commands/poll.mjs +3 -1
  39. package/bin/commands/ready-delay.d.mts +9 -4
  40. package/bin/commands/ready-delay.mjs +34 -20
  41. package/bin/commands/shepherd-journal.mjs +4 -1
  42. package/bin/commands/stack-drain.d.mts +35 -0
  43. package/bin/commands/stack-drain.mjs +129 -0
  44. package/bin/commands/stack-layer-readiness.d.mts +7 -0
  45. package/bin/commands/stack-layer-readiness.mjs +35 -0
  46. package/bin/commands/stack-stall.d.mts +14 -0
  47. package/bin/commands/stack-stall.mjs +69 -0
  48. package/bin/commands/stack-work.d.mts +32 -0
  49. package/bin/commands/stack-work.mjs +39 -0
  50. package/bin/config/load.d.mts +2 -0
  51. package/bin/config/load.mjs +10 -0
  52. package/bin/config.json +1 -0
  53. package/bin/github/batch-parsers.mjs +1 -1
  54. package/bin/github/batch-raw-rules.d.mts +0 -3
  55. package/bin/github/batch-raw-types.d.mts +2 -0
  56. package/bin/github/errors.d.mts +5 -0
  57. package/bin/github/errors.mjs +4 -0
  58. package/bin/github/gql/batch-pr.gql +1 -0
  59. package/bin/github/gql/poll-stack-summary.gql +8 -2
  60. package/bin/github/gql/poll-stack-topology.gql +38 -0
  61. package/bin/github/gql/poll-summary-check-contexts.gql +34 -0
  62. package/bin/github/gql/poll-summary-check-page.gql +23 -0
  63. package/bin/github/gql/poll-summary-fragment.gql +5 -62
  64. package/bin/github/gql/pr-merge-policy.gql +0 -3
  65. package/bin/github/merge-queue-checks.mjs +10 -1
  66. package/bin/github/poll-summary-check-hydration.d.mts +12 -0
  67. package/bin/github/poll-summary-check-hydration.mjs +55 -0
  68. package/bin/github/poll-summary-fingerprint.mjs +24 -4
  69. package/bin/github/poll-summary-projector.mjs +8 -7
  70. package/bin/github/poll-summary-queue-removal.mjs +10 -1
  71. package/bin/github/poll-summary-raw.d.mts +19 -31
  72. package/bin/github/poll-summary-route.mjs +8 -5
  73. package/bin/github/poll-summary.mjs +16 -73
  74. package/bin/github/queries.d.mts +7 -0
  75. package/bin/github/queries.mjs +9 -1
  76. package/bin/github/queue-removal-freshness.d.mts +16 -0
  77. package/bin/github/queue-removal-freshness.mjs +26 -0
  78. package/bin/github/stack-read.d.mts +34 -0
  79. package/bin/github/stack-read.mjs +92 -0
  80. package/bin/log/log-file.d.mts +1 -1
  81. package/bin/log/log-file.mjs +4 -17
  82. package/bin/state/base.d.mts +18 -1
  83. package/bin/state/base.mjs +65 -13
  84. package/bin/state/fix-attempts.d.mts +1 -1
  85. package/bin/state/fix-attempts.mjs +1 -1
  86. package/bin/state/graphql-quota-warnings.mjs +2 -6
  87. package/bin/state/iterate-stall.d.mts +7 -15
  88. package/bin/state/iterate-stall.mjs +6 -64
  89. package/bin/state/rest-cache.d.mts +1 -1
  90. package/bin/state/rest-cache.mjs +1 -1
  91. package/bin/state/stack-stall.d.mts +16 -0
  92. package/bin/state/stack-stall.mjs +12 -0
  93. package/bin/state/stall-state-store.d.mts +37 -0
  94. package/bin/state/stall-state-store.mjs +74 -0
  95. package/bin/types/escalate.d.mts +1 -1
  96. package/bin/types/github.d.mts +1 -1
  97. package/bin/types/iterate.d.mts +2 -1
  98. package/bin/types/merge-requirements.d.mts +19 -0
  99. package/bin/types/poll-summary.d.mts +5 -0
  100. package/bin/types/report.d.mts +1 -1
  101. package/package.json +1 -1
  102. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  103. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  104. package/plugins/pr-shepherd/.mcp.json +1 -1
@@ -1,8 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
- import { resolveStateBase } from "./base.mjs";
5
- import { SAFE_SEGMENT } from "../util/path-segment.mjs";
4
+ import { resolveRepoStateDir } from "./base.mjs";
6
5
  import { getWorktreeKey } from "../util/worktree.mjs";
7
6
  import { claimWarning } from "./graphql-quota-claims.mjs";
8
7
  import { evaluateGraphqlQuotaWarning, } from "./graphql-quota-policy.mjs";
@@ -52,9 +51,6 @@ async function serializeStateUpdate(key, update) {
52
51
  return result;
53
52
  }
54
53
  async function warningStatePath(key) {
55
- if (!SAFE_SEGMENT.test(key.owner) || !SAFE_SEGMENT.test(key.repo)) {
56
- throw new Error(`Invalid repo key segments: ${key.owner}/${key.repo}`);
57
- }
58
54
  let worktreeKey;
59
55
  try {
60
56
  worktreeKey = await getWorktreeKey();
@@ -62,7 +58,7 @@ async function warningStatePath(key) {
62
58
  catch {
63
59
  return undefined;
64
60
  }
65
- return join(resolveStateBase(), `${key.owner}-${key.repo}`, "worktrees", `${worktreeKey}-graphql-quota-warnings.json`);
61
+ return join(resolveRepoStateDir(key), "worktrees", `${worktreeKey}-graphql-quota-warnings.json`);
66
62
  }
67
63
  async function readState(path) {
68
64
  try {
@@ -5,23 +5,15 @@
5
5
  * was first seen. If the fingerprint does not change for stallTimeoutSeconds
6
6
  * the iterate command escalates instead of repeating the same action.
7
7
  *
8
- * State lives in `$TMPDIR/pr-shepherd-state/<owner>-<repo>/<pr>/iterate-stall.json`.
8
+ * State lives in `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/iterate-stall.json`.
9
9
  */
10
- interface StallState {
11
- /** Canonicalized JSON fingerprint of the material iterate inputs. */
12
- fingerprint: string;
13
- /** Unix timestamp (seconds) when this fingerprint was first seen. */
14
- firstSeenAt: number;
15
- }
16
- interface StateKey {
10
+ import { type StallStateStore } from "./stall-state-store.mts";
11
+ type Store = StallStateStore<{
17
12
  owner: string;
18
13
  repo: string;
19
14
  pr: number;
20
- }
21
- /** Read the current stall state. Returns null on miss, corrupt data, or invalid shape. */
22
- export declare function readStallState(key: StateKey): Promise<StallState | null>;
23
- /** Clear stall state so the next invocation starts a fresh timer (fire-and-forget — never throws). */
24
- export declare function clearStallState(key: StateKey): Promise<void>;
25
- /** Write stall state (fire-and-forget — never throws). */
26
- export declare function writeStallState(key: StateKey, state: StallState): Promise<void>;
15
+ }>;
16
+ export declare const readStallState: Store["read"];
17
+ export declare const writeStallState: Store["write"];
18
+ export declare const clearStallState: Store["clear"];
27
19
  export {};
@@ -5,69 +5,11 @@
5
5
  * was first seen. If the fingerprint does not change for stallTimeoutSeconds
6
6
  * the iterate command escalates instead of repeating the same action.
7
7
  *
8
- * State lives in `$TMPDIR/pr-shepherd-state/<owner>-<repo>/<pr>/iterate-stall.json`.
8
+ * State lives in `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/iterate-stall.json`.
9
9
  */
10
- import { readFile, writeFile, rename, unlink, mkdir } from "node:fs/promises";
11
- import { randomUUID } from "node:crypto";
12
- import { dirname } from "node:path";
13
10
  import { resolvePrStatePath } from "./base.mjs";
14
- // ---------------------------------------------------------------------------
15
- // Public API
16
- // ---------------------------------------------------------------------------
17
- /** Read the current stall state. Returns null on miss, corrupt data, or invalid shape. */
18
- export async function readStallState(key) {
19
- try {
20
- const raw = await readFile(resolvePath(key), "utf8");
21
- const parsed = JSON.parse(raw);
22
- if (parsed === null ||
23
- typeof parsed !== "object" ||
24
- typeof parsed["fingerprint"] !== "string" ||
25
- !Number.isFinite(parsed["firstSeenAt"])) {
26
- return null;
27
- }
28
- return parsed;
29
- }
30
- catch {
31
- return null;
32
- }
33
- }
34
- /** Clear stall state so the next invocation starts a fresh timer (fire-and-forget — never throws). */
35
- export async function clearStallState(key) {
36
- try {
37
- await unlink(resolvePath(key));
38
- }
39
- catch {
40
- // Best-effort — file may not exist.
41
- }
42
- }
43
- /** Write stall state (fire-and-forget — never throws). */
44
- export async function writeStallState(key, state) {
45
- let tmp;
46
- try {
47
- const path = resolvePath(key);
48
- tmp = `${path}.${randomUUID()}.tmp`;
49
- await mkdir(dirname(path), { recursive: true });
50
- await writeFile(tmp, JSON.stringify(state), "utf8");
51
- await rename(tmp, path);
52
- tmp = undefined;
53
- }
54
- catch {
55
- // Best-effort.
56
- }
57
- finally {
58
- if (tmp !== undefined) {
59
- try {
60
- await unlink(tmp);
61
- }
62
- catch {
63
- // Best-effort cleanup.
64
- }
65
- }
66
- }
67
- }
68
- // ---------------------------------------------------------------------------
69
- // Helpers
70
- // ---------------------------------------------------------------------------
71
- function resolvePath(key) {
72
- return resolvePrStatePath(key, "iterate-stall.json");
73
- }
11
+ import { stallStateStore } from "./stall-state-store.mjs";
12
+ const store = stallStateStore((key) => resolvePrStatePath(key, "iterate-stall.json"));
13
+ export const readStallState = store.read;
14
+ export const writeStallState = store.write;
15
+ export const clearStallState = store.clear;
@@ -14,7 +14,7 @@
14
14
  * terminal, check-run annotations once the check is COMPLETED. No ETag
15
15
  * applies; the cache is keyed on an immutable identity instead.
16
16
  *
17
- * Entries live under `$PR_SHEPHERD_STATE_DIR/<owner>-<repo>/<pr>/rest-cache/`
17
+ * Entries live under `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/rest-cache/`
18
18
  * and are removed for free when `pr-shepherd clean` deletes the PR's state
19
19
  * directory — there is no separate pruning routine.
20
20
  */
@@ -14,7 +14,7 @@
14
14
  * terminal, check-run annotations once the check is COMPLETED. No ETag
15
15
  * applies; the cache is keyed on an immutable identity instead.
16
16
  *
17
- * Entries live under `$PR_SHEPHERD_STATE_DIR/<owner>-<repo>/<pr>/rest-cache/`
17
+ * Entries live under `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/rest-cache/`
18
18
  * and are removed for free when `pr-shepherd clean` deletes the PR's state
19
19
  * directory — there is no separate pruning routine.
20
20
  */
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Persistent stall-detection state for an aggregate `--stack` selection whose open layers can
3
+ * only wait. Keyed by native stack number because the anchor PR can merge or change.
4
+ *
5
+ * State lives in `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/stack-<number>/stack-stall.json`.
6
+ */
7
+ import { type StallStateStore } from "./stall-state-store.mts";
8
+ type Store = StallStateStore<{
9
+ owner: string;
10
+ repo: string;
11
+ stack: number;
12
+ }>;
13
+ export declare const readStackStallState: Store["read"];
14
+ export declare const writeStackStallState: Store["write"];
15
+ export declare const clearStackStallState: Store["clear"];
16
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Persistent stall-detection state for an aggregate `--stack` selection whose open layers can
3
+ * only wait. Keyed by native stack number because the anchor PR can merge or change.
4
+ *
5
+ * State lives in `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/stack-<number>/stack-stall.json`.
6
+ */
7
+ import { resolveStackStatePath } from "./base.mjs";
8
+ import { stallStateStore } from "./stall-state-store.mjs";
9
+ const store = stallStateStore((key) => resolveStackStatePath(key, "stack-stall.json"));
10
+ export const readStackStallState = store.read;
11
+ export const writeStackStallState = store.write;
12
+ export const clearStackStallState = store.clear;
@@ -0,0 +1,37 @@
1
+ interface StallState {
2
+ /** Canonicalized JSON fingerprint of the material inputs. */
3
+ fingerprint: string;
4
+ /** Unix timestamp (seconds) when this fingerprint was first seen. */
5
+ firstSeenAt: number;
6
+ }
7
+ /** A missing or unreadable-as-JSON file is `state: null`. Any other I/O or key error is `ok: false`. */
8
+ type StallReadResult = {
9
+ ok: true;
10
+ state: StallState | null;
11
+ } | {
12
+ ok: false;
13
+ reason: string;
14
+ };
15
+ type StallWriteResult = {
16
+ ok: true;
17
+ } | {
18
+ ok: false;
19
+ reason: string;
20
+ };
21
+ export interface StallStateStore<Key> {
22
+ /** Read the current stall state. Does not throw. */
23
+ read(key: Key): Promise<StallReadResult>;
24
+ /** Write stall state. Does not throw; `ok: false` means the timer was not saved. */
25
+ write(key: Key, state: StallState): Promise<StallWriteResult>;
26
+ /** Clear stall state so the next invocation starts a fresh timer (fire-and-forget — never throws). */
27
+ clear(key: Key): Promise<void>;
28
+ }
29
+ /**
30
+ * `{fingerprint, firstSeenAt}` files behind the one-PR and `--stack` stall guards.
31
+ * A missing file is a miss. An unwritable directory or an unsafe key is a persistence failure
32
+ * so the caller can hand off instead of treating every tick as the first sighting.
33
+ * `ENOENT` is the only read error treated as a miss. Corrupt JSON is also a miss, so one
34
+ * successful rewrite can start the timer again.
35
+ */
36
+ export declare function stallStateStore<Key>(resolvePath: (key: Key) => string): StallStateStore<Key>;
37
+ export {};
@@ -0,0 +1,74 @@
1
+ import { readFile, writeFile, rename, unlink, mkdir } from "node:fs/promises";
2
+ import { randomUUID } from "node:crypto";
3
+ import { dirname } from "node:path";
4
+ /**
5
+ * `{fingerprint, firstSeenAt}` files behind the one-PR and `--stack` stall guards.
6
+ * A missing file is a miss. An unwritable directory or an unsafe key is a persistence failure
7
+ * so the caller can hand off instead of treating every tick as the first sighting.
8
+ * `ENOENT` is the only read error treated as a miss. Corrupt JSON is also a miss, so one
9
+ * successful rewrite can start the timer again.
10
+ */
11
+ export function stallStateStore(resolvePath) {
12
+ return {
13
+ async read(key) {
14
+ try {
15
+ const parsed = JSON.parse(await readFile(resolvePath(key), "utf8"));
16
+ if (parsed === null ||
17
+ typeof parsed !== "object" ||
18
+ typeof parsed["fingerprint"] !== "string" ||
19
+ !Number.isFinite(parsed["firstSeenAt"])) {
20
+ return { ok: true, state: null };
21
+ }
22
+ return { ok: true, state: parsed };
23
+ }
24
+ catch (error) {
25
+ // A missing file and corrupt JSON are both misses. One successful rewrite can start the timer.
26
+ if (isEnoent(error) || error instanceof SyntaxError)
27
+ return { ok: true, state: null };
28
+ return { ok: false, reason: errorReason(error) };
29
+ }
30
+ },
31
+ async write(key, state) {
32
+ let tmp;
33
+ try {
34
+ const path = resolvePath(key);
35
+ tmp = `${path}.${randomUUID()}.tmp`;
36
+ await mkdir(dirname(path), { recursive: true });
37
+ await writeFile(tmp, JSON.stringify(state), "utf8");
38
+ await rename(tmp, path);
39
+ tmp = undefined;
40
+ return { ok: true };
41
+ }
42
+ catch (error) {
43
+ return { ok: false, reason: errorReason(error) };
44
+ }
45
+ finally {
46
+ if (tmp !== undefined) {
47
+ try {
48
+ await unlink(tmp);
49
+ }
50
+ catch {
51
+ // Best-effort cleanup.
52
+ }
53
+ }
54
+ }
55
+ },
56
+ async clear(key) {
57
+ try {
58
+ await unlink(resolvePath(key));
59
+ }
60
+ catch {
61
+ // Best-effort — file may not exist. A leftover file can only make a later timer escalate sooner.
62
+ }
63
+ },
64
+ };
65
+ }
66
+ function isEnoent(error) {
67
+ return (typeof error === "object" &&
68
+ error !== null &&
69
+ "code" in error &&
70
+ error.code === "ENOENT");
71
+ }
72
+ function errorReason(error) {
73
+ return error instanceof Error ? error.message : String(error);
74
+ }
@@ -2,7 +2,7 @@ import type { AgentCheck, AgentComment, AgentThread } from "./report.mts";
2
2
  import type { ResolveCommand } from "./iterate.mts";
3
3
  import type { CheckStatus, Review } from "./github.mts";
4
4
  import type { MergeQueueRemovalStatus } from "./merge-requirements.mts";
5
- export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "check-follow-up-unavailable" | "authorization-required" | "merge-queue-removed";
5
+ export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "stall-state-unavailable" | "check-follow-up-unavailable" | "authorization-required" | "merge-queue-removed";
6
6
  export interface AgentStalledCheck {
7
7
  name: string;
8
8
  status: CheckStatus;
@@ -144,7 +144,7 @@ export interface BatchPrData extends BatchPrMergeFields {
144
144
  headRepoWithOwner: string | null;
145
145
  viewerAuthorization?: ViewerAuthorization;
146
146
  baseRefName: string;
147
- /** Git OID of the base branch tip observed with this PR, when available. */
147
+ /** Base commit GitHub recorded for this PR (`PullRequest.baseRefOid`), not the branch's live tip. */
148
148
  baseRefOid?: string;
149
149
  reviewRequests: Array<{
150
150
  login: string;
@@ -1,7 +1,7 @@
1
1
  import type { AgentThread, AgentComment, AgentCheck, GlobalOptions, RelevantCheck, ShepherdStatus, FirstLookThread, FirstLookComment } from "./report.mts";
2
2
  import type { ActiveCheck, PrActivitySummary } from "./activity.mts";
3
3
  import type { BranchProtection, MergeStateStatus, Review, ReviewDecision, ReviewThread, ShepherdMergeStatus } from "./github.mts";
4
- import type { MergeRequirements } from "./merge-requirements.mts";
4
+ import type { MergeRequirements, StackDraftHold } from "./merge-requirements.mts";
5
5
  import type { EscalateDetails } from "./escalate.mts";
6
6
  import type { MergeCommandPlan } from "./merge-action.mts";
7
7
  import type { ProtectedRun } from "./protected-run.mts";
@@ -50,6 +50,7 @@ interface IterateResultWait extends IterateResultBase {
50
50
  action: "wait";
51
51
  log: string;
52
52
  deferredWork?: import("./merge-queue.mts").IterateDeferredWork;
53
+ stackDraftHold?: StackDraftHold;
53
54
  }
54
55
  export type CancelReason = "merged" | "closed" | "ready-delay-elapsed";
55
56
  interface IterateResultCancel extends IterateResultBase {
@@ -39,6 +39,25 @@ export interface StackStatus {
39
39
  position: number;
40
40
  baseRefName: string;
41
41
  }
42
+ /** Why an open native stack layer does not yet let the layers above it advance. */
43
+ export type StackLayerBlockReason = "closed" | "draft" | "conflicting" | "queue-removal" | "failing-checks" | "review-work" | "checks-in-progress" | "merge-state" | "no-ready-receipt" | "stale-ancestry";
44
+ /** The lowest native stack layer that keeps an upper draft from being marked ready. */
45
+ export interface StackLowerLayerBlock {
46
+ pr: number;
47
+ reason: StackLayerBlockReason;
48
+ }
49
+ /**
50
+ * Why a native stack layer stays in draft on a WAIT tick. Repeating the same one-PR
51
+ * session cannot advance it, so the caller returns to the stack selector. A named
52
+ * `lowerLayer` is the layer to advance first; without one, the lower layers could not
53
+ * be verified.
54
+ */
55
+ export type StackDraftHold = {
56
+ kind: "lower-layer-not-ready";
57
+ lowerLayer?: StackLowerLayerBlock;
58
+ } | {
59
+ kind: "auto-mark-ready-disabled";
60
+ };
42
61
  /** Extra batch-PR fields for merge-queue, stacks, and folded branch rules. */
43
62
  export interface BatchPrMergeFields {
44
63
  branchRules?: BranchRules;
@@ -58,6 +58,11 @@ export interface PollSummaryItem {
58
58
  review?: PollSummaryReview;
59
59
  stack?: PollSummaryStack;
60
60
  pollCommand?: string;
61
+ /**
62
+ * `pollCommand` is one bounded tick without automatic mark-ready: it surfaces and routes review
63
+ * or CI work but cannot advance this draft, so repeating it for a waiting layer changes nothing.
64
+ */
65
+ pollProbe?: true;
61
66
  /** A current one-PR READY-after-delay completion was verified. */
62
67
  readyReceipt?: true;
63
68
  /** Lowest unready ancestor that prevents this layer from being stack-mergeable. */
@@ -31,7 +31,7 @@ export interface ShepherdReport {
31
31
  status: ShepherdStatus;
32
32
  /** PR base branch from the GraphQL batch. */
33
33
  baseBranch: string;
34
- /** Git OID of the base branch tip observed with this report, when available. */
34
+ /** Base commit GitHub recorded for this PR (`PullRequest.baseRefOid`), not the branch's live tip. */
35
35
  baseRefOid?: string;
36
36
  mergeStatus: MergeStatusResult;
37
37
  checks: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.52.0",
3
+ "version": "0.52.1",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
5
5
  "keywords": [
6
6
  "automation",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.52.0",
3
+ "version": "0.52.1",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for Codex.",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "pr-shepherd": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "pr-shepherd@0.52.0", "pr-shepherd-mcp"]
5
+ "args": ["--yes", "--package", "pr-shepherd@0.52.1", "pr-shepherd-mcp"]
6
6
  }
7
7
  }
8
8
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "pr-shepherd": {
3
3
  "command": "npx",
4
- "args": ["--yes", "--package", "pr-shepherd@0.52.0", "pr-shepherd-mcp"]
4
+ "args": ["--yes", "--package", "pr-shepherd@0.52.1", "pr-shepherd-mcp"]
5
5
  }
6
6
  }