pr-shepherd 0.30.0 → 0.30.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
3
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
4
- "version": "0.30.0",
4
+ "version": "0.30.1",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
@@ -7,7 +7,7 @@ import { checkEscalateTriggers, validateBaseBranch, buildEscalateSuggestion, bui
7
7
  import { buildResolveCommand } from "./classify.mjs";
8
8
  import { buildFixInstructions } from "./render.mjs";
9
9
  import { applyStallGuard } from "./stall.mjs";
10
- import { tryCancelRun, buildInProgressRunIds } from "./helpers.mjs";
10
+ import { tryCancelRun, buildAutoCancelRunIds, buildInProgressRunIds } from "./helpers.mjs";
11
11
  import { annotationMarkerBody } from "../check-annotations.mjs";
12
12
  import { threadTranscriptBody } from "../../threads/transcript.mjs";
13
13
  import { isHumanAuthor, isConfiguredBotAuthor } from "../../comments/authors.mjs";
@@ -78,10 +78,8 @@ export async function handleFixCode(ctx) {
78
78
  await writeFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber }, { headSha, threadAttempts, threadBodyHashes });
79
79
  let cancelled = [];
80
80
  if (!opts.noAutoCancelActionable) {
81
- const uniqueRunIds = [
82
- ...new Set(failingChecks.map((c) => c.runId).filter((id) => id !== null)),
83
- ];
84
- const results = await Promise.all(uniqueRunIds.map((id) => tryCancelRun(id, repoOwner, repoName)));
81
+ const runIds = buildAutoCancelRunIds(report);
82
+ const results = await Promise.all(runIds.map((id) => tryCancelRun(id, repoOwner, repoName)));
85
83
  cancelled = results.filter((id) => id !== null);
86
84
  }
87
85
  const cancelledSet = new Set(cancelled);
@@ -100,7 +98,11 @@ export async function handleFixCode(ctx) {
100
98
  hasConflicts ||
101
99
  changesRequestedReviews.length > 0 ||
102
100
  actionableComments.length > 0;
103
- const inProgressRunIds = pushLikely ? buildInProgressRunIds(report, cancelledSet) : [];
101
+ const inProgressRunIds = pushLikely
102
+ ? buildInProgressRunIds(report, cancelledSet, {
103
+ suppressProtectedFreshReruns: false,
104
+ })
105
+ : [];
104
106
  const commentMinimizeIds = report.comments.minimizeIds ?? actionableComments.map((c) => c.id);
105
107
  const allCommentIds = [...commentMinimizeIds, ...reviewSummaryIds];
106
108
  const { resolveCommand, resolveOnlyCommand } = buildResolveCommand(threads, resolutionOnlyThreads, allCommentIds, changesRequestedReviews, checks, prNumber, botUsernames, ruleAutoResolveThreadIds);
@@ -2,13 +2,7 @@ import { execFile as execFileCb } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { rest } from "../../github/http.mjs";
4
4
  const execFile = promisify(execFileCb);
5
- export function buildInProgressRunIds(report, cancelledSet) {
6
- return [
7
- ...new Set(report.checks.inProgress
8
- .map((c) => c.runId)
9
- .filter((id) => id !== null && !cancelledSet.has(id))),
10
- ];
11
- }
5
+ export { buildAutoCancelRunIds, buildInProgressRunIds } from "./reruns.mjs";
12
6
  export function buildSummary(report) {
13
7
  return {
14
8
  passing: report.checks.passing.length,
@@ -17,12 +11,7 @@ export function buildSummary(report) {
17
11
  inProgress: report.checks.inProgress.length,
18
12
  };
19
13
  }
20
- /**
21
- * Build the full list of CI checks relevant to PR readiness: triggered by a PR
22
- * event (or StatusContext with null event), completed, and not skipped/neutral.
23
- * Includes both passing and failing. Failing entries carry workflowName, jobName,
24
- * failedStep, and summary.
25
- */
14
+ /** Build completed, non-skipped checks relevant to PR readiness. */
26
15
  export function buildRelevantChecks(report) {
27
16
  const excluded = new Set([null, "SKIPPED", "NEUTRAL"]);
28
17
  const passing = report.checks.passing.flatMap((c) => {
@@ -0,0 +1,44 @@
1
+ function matchesRerunCheck(failure, check) {
2
+ if (failure.runId !== null && check.runId !== null) {
3
+ return failure.runId === check.runId && failure.name === check.name;
4
+ }
5
+ return failure.runId === null && check.runId === null && failure.name === check.name;
6
+ }
7
+ function isProtectedByFreshRerun(failure, check) {
8
+ const attemptStartedAt = check.startedAtUnix ?? check.updatedAtUnix ?? check.createdAtUnix;
9
+ return (attemptStartedAt !== undefined &&
10
+ matchesRerunCheck(failure, check) &&
11
+ failure.completedAtUnix !== undefined &&
12
+ attemptStartedAt >= failure.completedAtUnix &&
13
+ (failure.startedAtUnix === undefined || failure.startedAtUnix < attemptStartedAt));
14
+ }
15
+ function hasProtectedFreshRerun(failure, checks) {
16
+ return checks.some((check) => isProtectedByFreshRerun(failure, check));
17
+ }
18
+ function isProtectedFreshRerun(check, failures) {
19
+ const matchingFailures = failures.filter((failure) => matchesRerunCheck(failure, check));
20
+ return (matchingFailures.length > 0 &&
21
+ matchingFailures.every((failure) => isProtectedByFreshRerun(failure, check)));
22
+ }
23
+ function protectedFreshRerunIds(report) {
24
+ return new Set(report.checks.inProgress
25
+ .filter((check) => isProtectedFreshRerun(check, report.checks.failing))
26
+ .map((check) => check.runId)
27
+ .filter((id) => id !== null));
28
+ }
29
+ export function buildAutoCancelRunIds(report) {
30
+ return [
31
+ ...new Set(report.checks.failing
32
+ .filter((check) => !hasProtectedFreshRerun(check, report.checks.inProgress))
33
+ .map((check) => check.runId)
34
+ .filter((id) => id !== null)),
35
+ ];
36
+ }
37
+ export function buildInProgressRunIds(report, cancelledSet, opts = {}) {
38
+ const protectedRunIds = opts.suppressProtectedFreshReruns === false ? new Set() : protectedFreshRerunIds(report);
39
+ return [
40
+ ...new Set(report.checks.inProgress
41
+ .map((check) => check.runId)
42
+ .filter((id) => id !== null && !cancelledSet.has(id) && !protectedRunIds.has(id))),
43
+ ];
44
+ }
@@ -84,14 +84,11 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
84
84
  const event = node.checkSuite?.workflowRun?.event ?? null;
85
85
  const runId = extractRunId(node.detailsUrl);
86
86
  const summary = extractCheckRunSummary(node.title, node.summary);
87
- const rawCreatedAt = node.checkSuite
88
- ? (node.checkSuite.workflowRun?.createdAt ?? node.checkSuite.createdAt)
89
- : undefined;
90
- const rawUpdatedAt = node.checkSuite
91
- ? (node.checkSuite.workflowRun?.updatedAt ?? node.checkSuite.updatedAt)
92
- : undefined;
87
+ const rawCreatedAt = node.checkSuite?.workflowRun?.createdAt ?? node.checkSuite?.createdAt;
88
+ const rawUpdatedAt = node.checkSuite?.workflowRun?.updatedAt ?? node.checkSuite?.updatedAt;
93
89
  const createdAtUnix = rawCreatedAt ? parseCreatedAt(rawCreatedAt) : undefined;
94
90
  const startedAtUnix = node.startedAt ? parseCreatedAt(node.startedAt) : undefined;
91
+ const completedAtUnix = node.completedAt ? parseCreatedAt(node.completedAt) : undefined;
95
92
  const updatedAtUnix = rawUpdatedAt ? parseCreatedAt(rawUpdatedAt) : undefined;
96
93
  return [
97
94
  {
@@ -105,6 +102,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
105
102
  runId,
106
103
  ...(createdAtUnix !== undefined && { createdAtUnix }),
107
104
  ...(startedAtUnix !== undefined && { startedAtUnix }),
105
+ ...(completedAtUnix !== undefined && { completedAtUnix }),
108
106
  ...(updatedAtUnix !== undefined && { updatedAtUnix }),
109
107
  ...(summary !== undefined && { summary }),
110
108
  },
@@ -192,6 +192,7 @@ query BatchPr(
192
192
  status
193
193
  conclusion
194
194
  detailsUrl
195
+ completedAt
195
196
  startedAt
196
197
  title
197
198
  summary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Ong",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for Codex.",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",