pr-shepherd 0.54.0 → 0.55.0
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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -3
- package/bin/checks/unreported-required.d.mts +29 -0
- package/bin/checks/unreported-required.mjs +52 -0
- package/bin/classify/apply.d.mts +2 -0
- package/bin/classify/apply.mjs +31 -31
- package/bin/classify/rule-action.d.mts +8 -0
- package/bin/classify/rule-action.mjs +29 -0
- package/bin/classify/types.d.mts +1 -1
- package/bin/cli/help-command-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.mjs +6 -5
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +2 -2
- package/bin/cli/help.d.mts +3 -3
- package/bin/cli/iterate-branch-segment.d.mts +3 -0
- package/bin/cli/iterate-branch-segment.mjs +13 -0
- package/bin/cli/iterate-formatter.mjs +34 -22
- package/bin/cli/iterate-instructions.mjs +6 -0
- package/bin/cli/iterate-lean.mjs +16 -0
- package/bin/cli/poll-summary-emitter.mjs +4 -1
- package/bin/cli/poll-summary-formatter.mjs +10 -3
- package/bin/cli/stack-overview.d.mts +48 -0
- package/bin/cli/stack-overview.mjs +138 -0
- package/bin/commands/check-fingerprint.mjs +2 -1
- package/bin/commands/check-status.d.mts +1 -1
- package/bin/commands/check-status.mjs +8 -3
- package/bin/commands/check-unreported.d.mts +26 -0
- package/bin/commands/check-unreported.mjs +73 -0
- package/bin/commands/check.mjs +45 -38
- package/bin/commands/iterate/base.mjs +9 -0
- package/bin/commands/iterate/conflicting-head-ci.d.mts +18 -0
- package/bin/commands/iterate/conflicting-head-ci.mjs +32 -0
- package/bin/commands/iterate/escalate.mjs +4 -0
- package/bin/commands/iterate/fix-code.mjs +30 -1
- package/bin/commands/iterate/index.mjs +69 -3
- package/bin/commands/iterate/merge-state.mjs +12 -12
- package/bin/commands/iterate/merge.d.mts +12 -2
- package/bin/commands/iterate/merge.mjs +32 -2
- package/bin/commands/iterate/native-stack-rebase.d.mts +8 -2
- package/bin/commands/iterate/native-stack-rebase.mjs +14 -4
- package/bin/commands/iterate/stack-trunk-conflict.d.mts +13 -0
- package/bin/commands/iterate/stack-trunk-conflict.mjs +51 -0
- package/bin/commands/iterate/unreported-required.d.mts +18 -0
- package/bin/commands/iterate/unreported-required.mjs +122 -0
- package/bin/commands/poll-summary.mjs +1 -0
- package/bin/commands/poll.mjs +9 -2
- package/bin/commands/ready-mergeability.d.mts +1 -1
- package/bin/commands/ready-mergeability.mjs +2 -2
- package/bin/commands/rule-auto-resolve-format.d.mts +22 -0
- package/bin/commands/rule-auto-resolve-format.mjs +151 -0
- package/bin/commands/rule-auto-resolve.d.mts +21 -0
- package/bin/commands/rule-auto-resolve.mjs +113 -0
- package/bin/commands/stack-drain.d.mts +0 -5
- package/bin/commands/stack-drain.mjs +26 -16
- package/bin/commands/stack-merge-flag.d.mts +7 -0
- package/bin/commands/stack-merge-flag.mjs +6 -0
- package/bin/config/load.d.mts +3 -0
- package/bin/config/load.mjs +9 -2
- package/bin/config/merge-method.d.mts +27 -0
- package/bin/config/merge-method.mjs +56 -0
- package/bin/exit-codes.d.mts +1 -1
- package/bin/exit-codes.mjs +2 -1
- package/bin/github/batch-parse-suites.d.mts +5 -0
- package/bin/github/batch-parse-suites.mjs +20 -0
- package/bin/github/batch-parsers.d.mts +1 -1
- package/bin/github/batch-parsers.mjs +7 -0
- package/bin/github/batch-raw-types.d.mts +5 -0
- package/bin/github/batch.d.mts +5 -0
- package/bin/github/batch.mjs +10 -1
- package/bin/github/errors.d.mts +6 -0
- package/bin/github/errors.mjs +23 -5
- package/bin/github/fingerprint-fields.d.mts +1 -0
- package/bin/github/fingerprint-fields.mjs +1 -1
- package/bin/github/gql/batch-pr-page.gql +2 -0
- package/bin/github/gql/batch-pr.gql +5 -0
- package/bin/github/gql/commit-check-suites.gql +1 -0
- package/bin/github/gql/poll-stack-summary.gql +6 -0
- package/bin/github/gql/poll-summary-annotation-probe.gql +32 -0
- package/bin/github/gql/poll-summary-check-contexts.gql +2 -3
- package/bin/github/gql/poll-summary-fragment.gql +16 -0
- package/bin/github/gql/pr-merge-policy.gql +1 -44
- package/bin/github/gql/ref-rules-query.gql +18 -0
- package/bin/github/gql/ref-rules.gql +46 -0
- package/bin/github/gql/upper-layer-conflict-target.gql +36 -0
- package/bin/github/graphql-internal-retry.d.mts +1 -1
- package/bin/github/graphql-internal-retry.mjs +4 -3
- package/bin/github/merge-queue-checks.mjs +7 -3
- package/bin/github/merge-target-rules.d.mts +20 -0
- package/bin/github/merge-target-rules.mjs +54 -0
- package/bin/github/poll-summary-annotation-probe.d.mts +19 -0
- package/bin/github/poll-summary-annotation-probe.mjs +88 -0
- package/bin/github/poll-summary-fingerprint.mjs +1 -1
- package/bin/github/poll-summary-projector.d.mts +1 -1
- package/bin/github/poll-summary-projector.mjs +19 -12
- package/bin/github/poll-summary-queue-removal.mjs +7 -3
- package/bin/github/poll-summary-raw.d.mts +16 -0
- package/bin/github/poll-summary-readiness.mjs +1 -0
- package/bin/github/poll-summary-route.mjs +5 -0
- package/bin/github/poll-summary-unreported.d.mts +6 -0
- package/bin/github/poll-summary-unreported.mjs +51 -0
- package/bin/github/poll-summary.d.mts +1 -0
- package/bin/github/poll-summary.mjs +28 -3
- package/bin/github/queries.d.mts +12 -0
- package/bin/github/queries.mjs +14 -2
- package/bin/github/queue-removal-freshness.d.mts +20 -5
- package/bin/github/queue-removal-freshness.mjs +37 -10
- package/bin/github/stack-read.d.mts +4 -0
- package/bin/github/stack-read.mjs +13 -1
- package/bin/mcp/server.mjs +6 -1
- package/bin/state/ci-retrigger.d.mts +25 -0
- package/bin/state/ci-retrigger.mjs +48 -0
- package/bin/state/conflicting-head-seen.d.mts +13 -0
- package/bin/state/conflicting-head-seen.mjs +44 -0
- package/bin/types/escalate.d.mts +1 -1
- package/bin/types/github.d.mts +11 -0
- package/bin/types/iterate.d.mts +19 -3
- package/bin/types/poll-summary.d.mts +13 -0
- package/bin/types/report.d.mts +34 -1
- package/package.json +1 -1
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex.mcp.json +1 -1
- package/plugins/pr-shepherd/.mcp.json +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +2 -2
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +1 -1
- package/src/classify/types.mts +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { loadConfig } from "../config/load.mjs";
|
|
2
|
+
import { actionsWorkflowInProgress, selectMergeTargetContexts, unreportedRequiredContexts, } from "../checks/unreported-required.mjs";
|
|
3
|
+
import { parseBranchRules } from "./batch-parsers-rules.mjs";
|
|
4
|
+
/** Required contexts from the open layer whose base is the stack trunk. */
|
|
5
|
+
export function trunkRequiredContexts(ordered) {
|
|
6
|
+
const trunk = ordered.find((pr) => pr.stack)?.stack?.baseRefName;
|
|
7
|
+
if (!trunk)
|
|
8
|
+
return undefined;
|
|
9
|
+
const bottom = ordered.find((pr) => pr.state === "OPEN" && pr.baseRefName === trunk);
|
|
10
|
+
if (!bottom)
|
|
11
|
+
return undefined;
|
|
12
|
+
return parseBranchRules(bottom.baseRef).requiredStatusCheckContexts;
|
|
13
|
+
}
|
|
14
|
+
/** Record missing required contexts. A running Actions workflow does not clear the names. */
|
|
15
|
+
export function applyUnreportedRequiredChecks(raw, checks, mergeTargetContexts) {
|
|
16
|
+
const trunk = raw.stack?.baseRefName;
|
|
17
|
+
const required = selectMergeTargetContexts({
|
|
18
|
+
localContexts: parseBranchRules(raw.baseRef).requiredStatusCheckContexts,
|
|
19
|
+
...(mergeTargetContexts !== undefined && { trunkContexts: mergeTargetContexts }),
|
|
20
|
+
baseRefName: raw.baseRefName,
|
|
21
|
+
...(trunk !== undefined && mergeTargetContexts !== undefined && { trunkRefName: trunk }),
|
|
22
|
+
});
|
|
23
|
+
const missing = unreportedRequiredContexts(required, reportedNames(raw));
|
|
24
|
+
if (missing.length === 0)
|
|
25
|
+
return;
|
|
26
|
+
checks.unreportedRequired = missing;
|
|
27
|
+
const suites = raw.commits.nodes[0]?.commit.checkSuites?.nodes ?? [];
|
|
28
|
+
if (actionsWorkflowInProgress(suites, new Set(loadConfig().checks.ciTriggerEvents))) {
|
|
29
|
+
checks.actionsWorkflowInProgress = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function reportedNames(raw) {
|
|
33
|
+
const names = new Set();
|
|
34
|
+
const rollups = [
|
|
35
|
+
raw.commits.nodes[0]?.commit.statusCheckRollup,
|
|
36
|
+
raw.mergeQueueEntry?.headCommit?.statusCheckRollup,
|
|
37
|
+
];
|
|
38
|
+
for (const rollup of rollups) {
|
|
39
|
+
for (const node of rollup?.contexts.nodes ?? []) {
|
|
40
|
+
if (!node)
|
|
41
|
+
continue;
|
|
42
|
+
const name = node.__typename === "CheckRun" ? node.name : node.context;
|
|
43
|
+
if (typeof name !== "string")
|
|
44
|
+
continue;
|
|
45
|
+
const trimmed = name.trim();
|
|
46
|
+
if (trimmed)
|
|
47
|
+
names.add(trimmed);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return names;
|
|
51
|
+
}
|
|
@@ -5,6 +5,7 @@ export interface FetchedPollSummary {
|
|
|
5
5
|
selection: PollSummarySelection;
|
|
6
6
|
prs: PollSummaryItem[];
|
|
7
7
|
stackAncestry?: PollSummaryStackAncestry[];
|
|
8
|
+
allowedMergeMethods?: import("../config/merge-method.mts").MergeMethod[];
|
|
8
9
|
}
|
|
9
10
|
export declare function fetchPollSummary(opts: PollSummaryCommandOptions, repo: RepoInfo): Promise<FetchedPollSummary>;
|
|
10
11
|
/** Fresh, read-only snapshot used to bind a one-PR READY receipt to stack routing. */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
2
2
|
import { graphqlWithRateLimit } from "./client.mjs";
|
|
3
|
-
import { missingRepositoryError } from "./errors.mjs";
|
|
3
|
+
import { GitHubRequestError, isRetryableGraphQlResourceLimit, missingRepositoryError, } from "./errors.mjs";
|
|
4
4
|
import { hydratePollSummaryChecks } from "./poll-summary-check-hydration.mjs";
|
|
5
5
|
import { summarizePollSummaryPr } from "./poll-summary-projector.mjs";
|
|
6
|
+
import { trunkRequiredContexts } from "./poll-summary-unreported.mjs";
|
|
6
7
|
import { POLL_STACK_SUMMARY_QUERY, POLL_SUMMARY_FRAGMENT } from "./queries.mjs";
|
|
7
8
|
import { MAX_STACK_ENTRIES_PER_PAGE, readStack, readStackTopology, stackAncestryGaps, } from "./stack-read.mjs";
|
|
8
9
|
const MAX_EXPLICIT_PRS_PER_QUERY = 50;
|
|
@@ -62,16 +63,40 @@ async function fetchExplicitChunk(prs, repo) {
|
|
|
62
63
|
async function fetchStackSummary(opts, repo) {
|
|
63
64
|
const anchor = opts.stackPrNumber;
|
|
64
65
|
const topology = await readStackTopology(anchor, repo);
|
|
65
|
-
const { stackNumber, stackSize, viewerCanAdminister, ordered } = await
|
|
66
|
+
const { stackNumber, stackSize, viewerLogin, viewerCanAdminister, ordered, allowedMergeMethods } = await readStackSummary(anchor, repo, topology.stackSize);
|
|
66
67
|
for (const pr of ordered)
|
|
67
68
|
await hydratePollSummaryChecks(pr, repo);
|
|
68
69
|
const stackAncestry = stackAncestryGaps(ordered);
|
|
70
|
+
const required = trunkRequiredContexts(ordered);
|
|
69
71
|
return {
|
|
70
72
|
selection: { kind: "stack", anchor, stackNumber, stackSize },
|
|
71
|
-
prs: await Promise.all(ordered.map((pr) => summarizePollSummaryPr(pr, repo, opts, viewerCanAdminister))),
|
|
73
|
+
prs: await Promise.all(ordered.map((pr) => summarizePollSummaryPr(pr, repo, opts, viewerCanAdminister, required, viewerLogin))),
|
|
72
74
|
...(stackAncestry.length > 0 && { stackAncestry }),
|
|
75
|
+
...(allowedMergeMethods && { allowedMergeMethods }),
|
|
73
76
|
};
|
|
74
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* A wide check matrix can blow GitHub's per-query resource limit on the full
|
|
80
|
+
* page. Halve `first` down to one entry and let `readStack` follow `after`.
|
|
81
|
+
* The shared summary fragment stays intact: one layer is the same shape as an
|
|
82
|
+
* explicit summary, and check hydration still completes windows past 100.
|
|
83
|
+
*/
|
|
84
|
+
async function readStackSummary(anchor, repo, stackSize) {
|
|
85
|
+
let first = Math.min(stackSize, MAX_STACK_ENTRIES_PER_PAGE);
|
|
86
|
+
for (;;) {
|
|
87
|
+
try {
|
|
88
|
+
return await readStack(POLL_STACK_SUMMARY_QUERY, anchor, repo, { first });
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
if (!(err instanceof GitHubRequestError) ||
|
|
92
|
+
!isRetryableGraphQlResourceLimit(err.graphqlErrors) ||
|
|
93
|
+
first <= 1) {
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
first = Math.floor(first / 2);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
75
100
|
function deduplicate(values) {
|
|
76
101
|
return [...new Set(values)];
|
|
77
102
|
}
|
package/bin/github/queries.d.mts
CHANGED
|
@@ -14,15 +14,27 @@ export declare const BATCH_PR_PAGE_QUERY: string;
|
|
|
14
14
|
export declare const PR_FINGERPRINT_QUERY: string;
|
|
15
15
|
/** Compact per-PR fields shared by explicit-list and native-stack summary queries. */
|
|
16
16
|
export declare const POLL_SUMMARY_FRAGMENT: string;
|
|
17
|
+
/** Trunk branch rules plus how far `headRef` is behind that branch. */
|
|
18
|
+
export declare const REF_RULES_QUERY: string;
|
|
17
19
|
/**
|
|
18
20
|
* Pages older status contexts for one commit with the same node selection as
|
|
19
21
|
* the compact summary, so hydrated and first-page nodes fingerprint alike.
|
|
20
22
|
*/
|
|
21
23
|
export declare const POLL_SUMMARY_CHECK_PAGE_QUERY: string;
|
|
24
|
+
/**
|
|
25
|
+
* Annotation totals for one commit, used only when a summary layer otherwise
|
|
26
|
+
* looks ready. `$before` pages older than the newest 100 contexts.
|
|
27
|
+
*/
|
|
28
|
+
export declare const POLL_SUMMARY_ANNOTATION_PROBE_QUERY: string;
|
|
22
29
|
/** Discovers and summarizes every entry in one native GitHub pull-request stack. */
|
|
23
30
|
export declare const POLL_STACK_SUMMARY_QUERY: string;
|
|
24
31
|
/** Reads only native-stack membership and the refs that link adjacent entries. */
|
|
25
32
|
export declare const POLL_STACK_TOPOLOGY_QUERY: string;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a conflicting upper stack layer already contains its PR base, plus
|
|
35
|
+
* the open stack entries needed to name the bottom layer. Not part of BatchPr.
|
|
36
|
+
*/
|
|
37
|
+
export declare const UPPER_LAYER_CONFLICT_TARGET_QUERY: string;
|
|
26
38
|
/** PR head fields plus a single review thread for `commit-suggestion`. */
|
|
27
39
|
export declare const SUGGESTION_THREADS_QUERY: string;
|
|
28
40
|
/** Fetches additional comments for a single review thread when its nested connection paginates. */
|
package/bin/github/queries.mjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { readFileSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
const gql = (name) => readFileSync(join(import.meta.dirname, "gql", name), "utf8");
|
|
12
|
-
const withSharedFragments = (query) => `${gql("pr-merge-policy.gql")}\n${gql("commit-check-suites.gql")}\n${query}`;
|
|
12
|
+
const withSharedFragments = (query) => `${gql("ref-rules.gql")}\n${gql("pr-merge-policy.gql")}\n${gql("commit-check-suites.gql")}\n${query}`;
|
|
13
13
|
/** The primary batch query that fetches CI + comments + merge status in one round-trip. */
|
|
14
14
|
export const BATCH_PR_QUERY = withSharedFragments(gql("batch-pr.gql"));
|
|
15
15
|
/** Slim @include follow-up for outstanding batch-query connections. */
|
|
@@ -18,16 +18,28 @@ export const BATCH_PR_PAGE_QUERY = gql("batch-pr-page.gql");
|
|
|
18
18
|
export const PR_FINGERPRINT_QUERY = withSharedFragments(gql("pr-fingerprint.gql"));
|
|
19
19
|
const POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT = gql("poll-summary-check-contexts.gql");
|
|
20
20
|
/** Compact per-PR fields shared by explicit-list and native-stack summary queries. */
|
|
21
|
-
export const POLL_SUMMARY_FRAGMENT = `${POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT}\n${gql("poll-summary-fragment.gql")}`;
|
|
21
|
+
export const POLL_SUMMARY_FRAGMENT = `${gql("ref-rules.gql")}\n${POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT}\n${gql("poll-summary-fragment.gql")}`;
|
|
22
|
+
/** Trunk branch rules plus how far `headRef` is behind that branch. */
|
|
23
|
+
export const REF_RULES_QUERY = `${gql("ref-rules.gql")}\n${gql("ref-rules-query.gql")}`;
|
|
22
24
|
/**
|
|
23
25
|
* Pages older status contexts for one commit with the same node selection as
|
|
24
26
|
* the compact summary, so hydrated and first-page nodes fingerprint alike.
|
|
25
27
|
*/
|
|
26
28
|
export const POLL_SUMMARY_CHECK_PAGE_QUERY = `${POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT}\n${gql("poll-summary-check-page.gql")}`;
|
|
29
|
+
/**
|
|
30
|
+
* Annotation totals for one commit, used only when a summary layer otherwise
|
|
31
|
+
* looks ready. `$before` pages older than the newest 100 contexts.
|
|
32
|
+
*/
|
|
33
|
+
export const POLL_SUMMARY_ANNOTATION_PROBE_QUERY = gql("poll-summary-annotation-probe.gql");
|
|
27
34
|
/** Discovers and summarizes every entry in one native GitHub pull-request stack. */
|
|
28
35
|
export const POLL_STACK_SUMMARY_QUERY = `${POLL_SUMMARY_FRAGMENT}\n${gql("poll-stack-summary.gql")}`;
|
|
29
36
|
/** Reads only native-stack membership and the refs that link adjacent entries. */
|
|
30
37
|
export const POLL_STACK_TOPOLOGY_QUERY = gql("poll-stack-topology.gql");
|
|
38
|
+
/**
|
|
39
|
+
* Whether a conflicting upper stack layer already contains its PR base, plus
|
|
40
|
+
* the open stack entries needed to name the bottom layer. Not part of BatchPr.
|
|
41
|
+
*/
|
|
42
|
+
export const UPPER_LAYER_CONFLICT_TARGET_QUERY = gql("upper-layer-conflict-target.gql");
|
|
31
43
|
/** PR head fields plus a single review thread for `commit-suggestion`. */
|
|
32
44
|
export const SUGGESTION_THREADS_QUERY = gql("suggestion-threads.gql");
|
|
33
45
|
/** Fetches additional comments for a single review thread when its nested connection paginates. */
|
|
@@ -1,16 +1,31 @@
|
|
|
1
|
+
type PushCheckNode = {
|
|
2
|
+
__typename?: string;
|
|
3
|
+
checkSuite?: {
|
|
4
|
+
createdAt?: string | null;
|
|
5
|
+
workflowRun?: {
|
|
6
|
+
event?: string | null;
|
|
7
|
+
createdAt?: string | null;
|
|
8
|
+
} | null;
|
|
9
|
+
} | null;
|
|
10
|
+
};
|
|
1
11
|
/**
|
|
2
12
|
* Whether a removed merge-queue commit still belongs to the current PR head.
|
|
3
13
|
*
|
|
4
14
|
* A merge-commit queue lists that head as a parent. Squash and rebase queues
|
|
5
|
-
* build a one-parent commit on the base
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
15
|
+
* build a one-parent commit on the base, so parent identity cannot show a
|
|
16
|
+
* later push. Missing parents are unverifiable: GitHub keeps returning the
|
|
17
|
+
* latest removal after the synthetic commit is gone. A one-parent removal is
|
|
18
|
+
* stale once this head reached the PR after the removal. That time is the
|
|
19
|
+
* earliest pull_request check on the head, with the head's committer time as
|
|
20
|
+
* the fallback when no check time is available.
|
|
10
21
|
*/
|
|
11
22
|
export declare function queueRemovalAppliesToHead(input: {
|
|
12
23
|
parentOids: readonly string[] | null | undefined;
|
|
13
24
|
headOid: string;
|
|
14
25
|
headCommittedAtUnix?: number;
|
|
26
|
+
headPushedAtUnix?: number;
|
|
15
27
|
removedAtUnix?: number;
|
|
16
28
|
}): boolean;
|
|
29
|
+
/** Earliest pull_request check-suite time on the head commit, when one exists. */
|
|
30
|
+
export declare function headPushUnixFromCheckNodes(nodes: readonly (PushCheckNode | null)[] | null | undefined): number | undefined;
|
|
31
|
+
export {};
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { parseCreatedAt } from "./batch-parser-helpers.mjs";
|
|
2
|
+
const PULL_REQUEST_EVENTS = new Set(["pull_request", "pull_request_target"]);
|
|
1
3
|
/**
|
|
2
4
|
* Whether a removed merge-queue commit still belongs to the current PR head.
|
|
3
5
|
*
|
|
4
6
|
* A merge-commit queue lists that head as a parent. Squash and rebase queues
|
|
5
|
-
* build a one-parent commit on the base
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* build a one-parent commit on the base, so parent identity cannot show a
|
|
8
|
+
* later push. Missing parents are unverifiable: GitHub keeps returning the
|
|
9
|
+
* latest removal after the synthetic commit is gone. A one-parent removal is
|
|
10
|
+
* stale once this head reached the PR after the removal. That time is the
|
|
11
|
+
* earliest pull_request check on the head, with the head's committer time as
|
|
12
|
+
* the fallback when no check time is available.
|
|
10
13
|
*/
|
|
11
14
|
export function queueRemovalAppliesToHead(input) {
|
|
12
15
|
const parents = input.parentOids ?? [];
|
|
@@ -16,11 +19,35 @@ export function queueRemovalAppliesToHead(input) {
|
|
|
16
19
|
return true;
|
|
17
20
|
if (parents.length > 1)
|
|
18
21
|
return false;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const removedAt = input.removedAtUnix;
|
|
23
|
+
if (removedAt === undefined || removedAt <= 0)
|
|
24
|
+
return true;
|
|
25
|
+
const pushedAt = input.headPushedAtUnix;
|
|
26
|
+
if (pushedAt !== undefined && pushedAt > 0)
|
|
27
|
+
return pushedAt <= removedAt;
|
|
28
|
+
const committedAt = input.headCommittedAtUnix;
|
|
29
|
+
if (committedAt !== undefined && committedAt > removedAt)
|
|
23
30
|
return false;
|
|
24
|
-
}
|
|
25
31
|
return true;
|
|
26
32
|
}
|
|
33
|
+
/** Earliest pull_request check-suite time on the head commit, when one exists. */
|
|
34
|
+
export function headPushUnixFromCheckNodes(nodes) {
|
|
35
|
+
let earliest;
|
|
36
|
+
for (const node of nodes ?? []) {
|
|
37
|
+
if (node?.__typename !== "CheckRun")
|
|
38
|
+
continue;
|
|
39
|
+
const run = node.checkSuite?.workflowRun;
|
|
40
|
+
const event = run?.event;
|
|
41
|
+
if (event == null || !PULL_REQUEST_EVENTS.has(event))
|
|
42
|
+
continue;
|
|
43
|
+
const raw = node.checkSuite?.createdAt || run?.createdAt;
|
|
44
|
+
if (!raw)
|
|
45
|
+
continue;
|
|
46
|
+
const at = parseCreatedAt(raw);
|
|
47
|
+
if (at <= 0)
|
|
48
|
+
continue;
|
|
49
|
+
if (earliest === undefined || at < earliest)
|
|
50
|
+
earliest = at;
|
|
51
|
+
}
|
|
52
|
+
return earliest;
|
|
53
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MergeMethod } from "../config/merge-method.mts";
|
|
1
2
|
import type { PollSummaryStackAncestry } from "../types.mts";
|
|
2
3
|
import { type RepoInfo } from "./client.mts";
|
|
3
4
|
/** GitHub's `first` ceiling for one native-stack entries page. */
|
|
@@ -15,9 +16,12 @@ export interface StackMemberRefs {
|
|
|
15
16
|
export interface StackRead<Pr extends StackMemberRefs> {
|
|
16
17
|
stackNumber: number;
|
|
17
18
|
stackSize: number;
|
|
19
|
+
viewerLogin: string | null;
|
|
18
20
|
viewerCanAdminister: boolean;
|
|
19
21
|
/** Every member, bottom-to-top, each observed in the same paged read. */
|
|
20
22
|
ordered: Pr[];
|
|
23
|
+
/** Present when the query selected repository merge settings, including an empty list. */
|
|
24
|
+
allowedMergeMethods?: MergeMethod[];
|
|
21
25
|
}
|
|
22
26
|
/**
|
|
23
27
|
* Page through one native stack and fail closed on anything short of the full
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readAllowedMergeMethods } from "../config/merge-method.mjs";
|
|
1
2
|
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
2
3
|
import { graphqlWithRateLimit } from "./client.mjs";
|
|
3
4
|
import { missingRepositoryError } from "./errors.mjs";
|
|
@@ -15,13 +16,17 @@ export async function readStack(query, anchor, repo, variables = {}) {
|
|
|
15
16
|
let stackNumber = 0;
|
|
16
17
|
let stackSize = 0;
|
|
17
18
|
let viewerCanAdminister = false;
|
|
19
|
+
let viewerLogin = null;
|
|
20
|
+
let allowedMergeMethods;
|
|
18
21
|
const entries = [];
|
|
19
22
|
do {
|
|
20
23
|
const response = await graphqlWithRateLimit(query, { owner: repo.owner, repo: repo.name, anchor, after, ...variables });
|
|
24
|
+
viewerLogin = response.data.viewer?.login ?? viewerLogin;
|
|
21
25
|
const repository = response.data.repository;
|
|
22
26
|
if (!repository)
|
|
23
27
|
throw missingRepositoryError(repo);
|
|
24
28
|
viewerCanAdminister = repository.viewerCanAdminister;
|
|
29
|
+
allowedMergeMethods = readAllowedMergeMethods(repository) ?? allowedMergeMethods;
|
|
25
30
|
if (!repository.pullRequest) {
|
|
26
31
|
throw new ShepherdError(`PR #${anchor} not found`, EXIT.UNAVAILABLE);
|
|
27
32
|
}
|
|
@@ -59,7 +64,14 @@ export async function readStack(query, anchor, repo, variables = {}) {
|
|
|
59
64
|
const ordered = [...unique.values()]
|
|
60
65
|
.sort((left, right) => left.position - right.position)
|
|
61
66
|
.map((entry) => entry.pullRequest);
|
|
62
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
stackNumber,
|
|
69
|
+
stackSize,
|
|
70
|
+
viewerLogin,
|
|
71
|
+
viewerCanAdminister,
|
|
72
|
+
ordered,
|
|
73
|
+
...(allowedMergeMethods && { allowedMergeMethods }),
|
|
74
|
+
};
|
|
63
75
|
}
|
|
64
76
|
/** Membership and linking refs only — no per-PR CI or review hydration. */
|
|
65
77
|
export function readStackTopology(anchor, repo) {
|
package/bin/mcp/server.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { isRepositoryQualifiedPrReference, normalizeRepositoryIdentity, parsePrR
|
|
|
7
7
|
import { formatJournalResult } from "../cli/journal-formatter.mjs";
|
|
8
8
|
import { formatCommitSuggestionResult, formatSuggestionPatchesResult, formatIterateResult, formatMarkFilesAsViewedResult, formatMutateResult, projectIterateLean, } from "../cli/formatters.mjs";
|
|
9
9
|
import { formatPollSummaryResult } from "../cli/poll-summary-formatter.mjs";
|
|
10
|
+
import { projectStackOverview } from "../cli/stack-overview.mjs";
|
|
10
11
|
import { formatCliError, serializeGitHubRequestErrorDetails } from "../cli/error-format.mjs";
|
|
11
12
|
import { errorToExitCode, EXIT } from "../exit-codes.mjs";
|
|
12
13
|
const QUALIFIED_PR_ERROR = "pr must be a GitHub pull-request URL or an owner/repo#number reference";
|
|
@@ -104,7 +105,11 @@ export function createPrShepherdMcpServer(options = {}) {
|
|
|
104
105
|
};
|
|
105
106
|
return runTool(() => runIterateSelector(shepherd, requireRepositoryQualifiedIterate(input)), (result) => isPollSummary(result)
|
|
106
107
|
? formatPollSummaryResult(result)
|
|
107
|
-
: formatIterateResult(result, opts), (result) => isPollSummary(result)
|
|
108
|
+
: formatIterateResult(result, opts), (result) => isPollSummary(result)
|
|
109
|
+
? result.selection.kind === "stack"
|
|
110
|
+
? projectStackOverview(result)
|
|
111
|
+
: result
|
|
112
|
+
: projectIterateLean(result, opts));
|
|
108
113
|
});
|
|
109
114
|
server.registerTool("apply", {
|
|
110
115
|
description: "Apply ordered review, journal, and file-view operations after prevalidation; explicit requests rely on GitHub's mutation response.",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One close/reopen of a head that was missing required checks.
|
|
3
|
+
*
|
|
4
|
+
* `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/ci-retrigger.json`
|
|
5
|
+
* Reopening does not change the head SHA, so the next tick must not close the PR again.
|
|
6
|
+
*/
|
|
7
|
+
interface Retrigger {
|
|
8
|
+
headSha: string;
|
|
9
|
+
contexts: string[];
|
|
10
|
+
}
|
|
11
|
+
/** The stored head and context set, or undefined when no usable marker exists. */
|
|
12
|
+
export declare function readCiRetrigger(key: {
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
pr: number;
|
|
16
|
+
}): Promise<Retrigger | undefined>;
|
|
17
|
+
/** Remember that this head was already closed and reopened for these contexts. */
|
|
18
|
+
export declare function writeCiRetrigger(key: {
|
|
19
|
+
owner: string;
|
|
20
|
+
repo: string;
|
|
21
|
+
pr: number;
|
|
22
|
+
}, record: Retrigger): Promise<void>;
|
|
23
|
+
/** True when this head was already retriggered for the same required contexts. */
|
|
24
|
+
export declare function sameCiRetrigger(record: Retrigger | undefined, headSha: string, contexts: readonly string[]): boolean;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One close/reopen of a head that was missing required checks.
|
|
3
|
+
*
|
|
4
|
+
* `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/ci-retrigger.json`
|
|
5
|
+
* Reopening does not change the head SHA, so the next tick must not close the PR again.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
import { resolvePrStatePath } from "./base.mjs";
|
|
10
|
+
/** The stored head and context set, or undefined when no usable marker exists. */
|
|
11
|
+
export async function readCiRetrigger(key) {
|
|
12
|
+
return readRetrigger(resolvePrStatePath(key, "ci-retrigger.json"));
|
|
13
|
+
}
|
|
14
|
+
/** Remember that this head was already closed and reopened for these contexts. */
|
|
15
|
+
export async function writeCiRetrigger(key, record) {
|
|
16
|
+
const path = resolvePrStatePath(key, "ci-retrigger.json");
|
|
17
|
+
await mkdir(dirname(path), { recursive: true });
|
|
18
|
+
const next = {
|
|
19
|
+
headSha: record.headSha,
|
|
20
|
+
contexts: [...record.contexts].sort(),
|
|
21
|
+
};
|
|
22
|
+
await writeFile(path, JSON.stringify(next));
|
|
23
|
+
}
|
|
24
|
+
/** True when this head was already retriggered for the same required contexts. */
|
|
25
|
+
export function sameCiRetrigger(record, headSha, contexts) {
|
|
26
|
+
if (!record || record.headSha !== headSha)
|
|
27
|
+
return false;
|
|
28
|
+
const stored = [...record.contexts].sort();
|
|
29
|
+
const current = [...contexts].sort();
|
|
30
|
+
return stored.length === current.length && stored.every((name, index) => name === current[index]);
|
|
31
|
+
}
|
|
32
|
+
async function readRetrigger(path) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
35
|
+
if (parsed === null || typeof parsed !== "object")
|
|
36
|
+
return undefined;
|
|
37
|
+
const headSha = parsed.headSha;
|
|
38
|
+
const contexts = parsed.contexts;
|
|
39
|
+
if (typeof headSha !== "string" || !Array.isArray(contexts))
|
|
40
|
+
return undefined;
|
|
41
|
+
if (!contexts.every((name) => typeof name === "string"))
|
|
42
|
+
return undefined;
|
|
43
|
+
return { headSha, contexts };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When Shepherd first saw a conflicting head that had no CI.
|
|
3
|
+
*
|
|
4
|
+
* `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/conflicting-head.json`
|
|
5
|
+
* GitHub's Commit object has no push timestamp, so the missing-CI grace
|
|
6
|
+
* starts at this observation instead of `committedDate`.
|
|
7
|
+
*/
|
|
8
|
+
/** Unix seconds of the first observation of `headSha`, or undefined when state cannot be used. */
|
|
9
|
+
export declare function conflictingHeadFirstSeenUnix(key: {
|
|
10
|
+
owner: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
pr: number;
|
|
13
|
+
}, headSha: string, nowMs: number): Promise<number | undefined>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When Shepherd first saw a conflicting head that had no CI.
|
|
3
|
+
*
|
|
4
|
+
* `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/conflicting-head.json`
|
|
5
|
+
* GitHub's Commit object has no push timestamp, so the missing-CI grace
|
|
6
|
+
* starts at this observation instead of `committedDate`.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
import { resolvePrStatePath } from "./base.mjs";
|
|
11
|
+
/** Unix seconds of the first observation of `headSha`, or undefined when state cannot be used. */
|
|
12
|
+
export async function conflictingHeadFirstSeenUnix(key, headSha, nowMs) {
|
|
13
|
+
const nowUnix = Math.floor(nowMs / 1000);
|
|
14
|
+
try {
|
|
15
|
+
const path = resolvePrStatePath(key, "conflicting-head.json");
|
|
16
|
+
const existing = await readSeen(path);
|
|
17
|
+
if (existing?.headSha === headSha)
|
|
18
|
+
return existing.firstSeenAtUnix;
|
|
19
|
+
await mkdir(dirname(path), { recursive: true });
|
|
20
|
+
const next = { headSha, firstSeenAtUnix: nowUnix };
|
|
21
|
+
await writeFile(path, JSON.stringify(next));
|
|
22
|
+
return nowUnix;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function readSeen(path) {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
31
|
+
if (parsed === null || typeof parsed !== "object")
|
|
32
|
+
return undefined;
|
|
33
|
+
const headSha = parsed.headSha;
|
|
34
|
+
const firstSeenAtUnix = parsed.firstSeenAtUnix;
|
|
35
|
+
if (typeof headSha !== "string" || typeof firstSeenAtUnix !== "number")
|
|
36
|
+
return undefined;
|
|
37
|
+
if (!Number.isFinite(firstSeenAtUnix))
|
|
38
|
+
return undefined;
|
|
39
|
+
return { headSha, firstSeenAtUnix };
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/bin/types/escalate.d.mts
CHANGED
|
@@ -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" | "stall-state-unavailable" | "check-follow-up-unavailable" | "authorization-required" | "merge-queue-removed";
|
|
5
|
+
export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "required-checks-unreported" | "stall-state-unavailable" | "check-follow-up-unavailable" | "authorization-required" | "merge-queue-removed" | "merge-method-unavailable";
|
|
6
6
|
export interface AgentStalledCheck {
|
|
7
7
|
name: string;
|
|
8
8
|
status: CheckStatus;
|
package/bin/types/github.d.mts
CHANGED
|
@@ -108,6 +108,8 @@ export interface Review {
|
|
|
108
108
|
staleBotCr?: boolean;
|
|
109
109
|
/** Commit OID the review was made against, when available from GraphQL. */
|
|
110
110
|
commitOid?: string;
|
|
111
|
+
/** Review HTML URL, when the batch query selected it. */
|
|
112
|
+
url?: string;
|
|
111
113
|
/** True when commitOid !== headRefOid AND all associated threads are resolved/outdated. Distinct from `staleBotCr` (time-based). Reviews with no threads are not marked stale. */
|
|
112
114
|
staleReview?: boolean;
|
|
113
115
|
}
|
|
@@ -139,10 +141,17 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
139
141
|
mergeStateStatus: MergeStateStatus;
|
|
140
142
|
reviewDecision: ReviewDecision;
|
|
141
143
|
headRefOid: string;
|
|
144
|
+
/**
|
|
145
|
+
* Earliest pull_request check-suite time on the head commit. Omitted when
|
|
146
|
+
* that signal is unavailable; queue-removal freshness then uses committer time.
|
|
147
|
+
*/
|
|
148
|
+
headPushedAtUnix?: number;
|
|
142
149
|
headRefName: string;
|
|
143
150
|
/** `"owner/name"` of the head repository; null when the fork has been deleted. */
|
|
144
151
|
headRepoWithOwner: string | null;
|
|
145
152
|
viewerAuthorization?: ViewerAuthorization;
|
|
153
|
+
/** GitHub `viewer.login` for the token that fetched this batch. */
|
|
154
|
+
viewerLogin?: string;
|
|
146
155
|
baseRefName: string;
|
|
147
156
|
/** Base commit GitHub recorded for this PR (`PullRequest.baseRefOid`), not the branch's live tip. */
|
|
148
157
|
baseRefOid?: string;
|
|
@@ -164,6 +173,8 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
164
173
|
removedMergeQueueChecks?: CheckRun[];
|
|
165
174
|
removedMergeQueueChecksIncomplete?: true;
|
|
166
175
|
branchProtection: BranchProtection | null;
|
|
176
|
+
/** Repository merge methods. Omitted when the batch did not select them. Empty when none are enabled. */
|
|
177
|
+
allowedMergeMethods?: import("../config/merge-method.mts").MergeMethod[];
|
|
167
178
|
activity?: PrActivitySummary;
|
|
168
179
|
}
|
|
169
180
|
export {};
|
package/bin/types/iterate.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentThread, AgentComment, AgentCheck, GlobalOptions, RelevantCheck, ShepherdStatus, FirstLookThread, FirstLookComment } from "./report.mts";
|
|
1
|
+
import type { AgentThread, AgentComment, AgentCheck, GlobalOptions, RelevantCheck, ShepherdStatus, FirstLookThread, FirstLookComment, RuleAutoResolveReport } 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
4
|
import type { MergeRequirements, StackDraftHold } from "./merge-requirements.mts";
|
|
@@ -6,7 +6,7 @@ import type { EscalateDetails } from "./escalate.mts";
|
|
|
6
6
|
import type { MergeCommandPlan } from "./merge-action.mts";
|
|
7
7
|
import type { ProtectedRun } from "./protected-run.mts";
|
|
8
8
|
import type { ApiUsage, GraphqlQuotaWarning } from "./api-usage.mts";
|
|
9
|
-
export type ShepherdAction = "wait" | "fix_code" | "mark_ready" | "merge" | "cancel" | "escalate";
|
|
9
|
+
export type ShepherdAction = "wait" | "ready" | "fix_code" | "mark_ready" | "merge" | "cancel" | "escalate";
|
|
10
10
|
export interface IterateResultSummary {
|
|
11
11
|
passing: number;
|
|
12
12
|
skipped: number;
|
|
@@ -29,6 +29,11 @@ export interface IterateResultBase {
|
|
|
29
29
|
summary: IterateResultSummary;
|
|
30
30
|
/** Validated base branch (e.g. "main") for this PR. */
|
|
31
31
|
baseBranch: string;
|
|
32
|
+
/**
|
|
33
|
+
* Set when a native-stack upper layer is conflicting but already contains its
|
|
34
|
+
* PR base, so the dirty state is against the stack trunk rather than that base.
|
|
35
|
+
*/
|
|
36
|
+
stackTrunkConflict?: string;
|
|
32
37
|
/** Null when no classic protection rule exists or the base ref is unavailable. */
|
|
33
38
|
branchProtection: BranchProtection | null;
|
|
34
39
|
mergeRequirements?: MergeRequirements;
|
|
@@ -40,11 +45,17 @@ export interface IterateResultBase {
|
|
|
40
45
|
inProgressChecks?: ActiveCheck[];
|
|
41
46
|
ignoredNames?: string[];
|
|
42
47
|
supersededNames?: string[];
|
|
48
|
+
/** Required merge-target contexts with no check run and no status context. Omitted when empty. */
|
|
49
|
+
unreportedRequiredChecks?: string[];
|
|
50
|
+
/** Commits on the stack trunk that the bottom open layer does not contain. Omitted when zero. */
|
|
51
|
+
trunkBehindBy?: number;
|
|
43
52
|
activity?: PrActivitySummary;
|
|
44
53
|
mergeQueue?: import("./merge-queue.mts").MergeQueueReport;
|
|
45
54
|
apiUsage?: ApiUsage;
|
|
46
55
|
quotaWarning?: GraphqlQuotaWarning;
|
|
47
56
|
fingerprintReused?: true;
|
|
57
|
+
/** Classification-rule resolve/minimize applied during this fetch. Omitted when nothing happened. */
|
|
58
|
+
ruleAutoResolve?: RuleAutoResolveReport;
|
|
48
59
|
}
|
|
49
60
|
interface IterateResultWait extends IterateResultBase {
|
|
50
61
|
action: "wait";
|
|
@@ -52,6 +63,11 @@ interface IterateResultWait extends IterateResultBase {
|
|
|
52
63
|
deferredWork?: import("./merge-queue.mts").IterateDeferredWork;
|
|
53
64
|
stackDraftHold?: StackDraftHold;
|
|
54
65
|
}
|
|
66
|
+
/** Clean PR whose ready-delay is still counting. `--until-terminal` returns this instead of sleeping. */
|
|
67
|
+
interface IterateResultReady extends IterateResultBase {
|
|
68
|
+
action: "ready";
|
|
69
|
+
log: string;
|
|
70
|
+
}
|
|
55
71
|
export type CancelReason = "merged" | "closed" | "ready-delay-elapsed";
|
|
56
72
|
interface IterateResultCancel extends IterateResultBase {
|
|
57
73
|
action: "cancel";
|
|
@@ -129,7 +145,7 @@ interface IterateResultEscalate extends IterateResultBase {
|
|
|
129
145
|
action: "escalate";
|
|
130
146
|
escalate: EscalateDetails;
|
|
131
147
|
}
|
|
132
|
-
export type IterateResult = IterateResultWait | IterateResultCancel | IterateResultFixCode | IterateResultMarkReady | IterateResultMerge | IterateResultEscalate;
|
|
148
|
+
export type IterateResult = IterateResultWait | IterateResultReady | IterateResultCancel | IterateResultFixCode | IterateResultMarkReady | IterateResultMerge | IterateResultEscalate;
|
|
133
149
|
export interface IterateCommandOptions extends GlobalOptions {
|
|
134
150
|
readyDelaySeconds?: number;
|
|
135
151
|
noAutoMarkReady?: boolean;
|
|
@@ -11,6 +11,10 @@ export interface PollSummaryChecks {
|
|
|
11
11
|
ignored?: number;
|
|
12
12
|
superseded?: number;
|
|
13
13
|
incomplete?: true;
|
|
14
|
+
/** Required contexts with no check run and no status context. Omitted when empty. */
|
|
15
|
+
unreportedRequired?: string[];
|
|
16
|
+
/** A relevant Actions workflow suite has not completed. */
|
|
17
|
+
actionsWorkflowInProgress?: true;
|
|
14
18
|
}
|
|
15
19
|
export interface PollSummaryReview {
|
|
16
20
|
comments?: number;
|
|
@@ -39,6 +43,10 @@ export interface PollSummaryItem {
|
|
|
39
43
|
repo: string;
|
|
40
44
|
title: string;
|
|
41
45
|
url: string;
|
|
46
|
+
/** GitHub author login, when the summary query returned one. */
|
|
47
|
+
authorLogin?: string;
|
|
48
|
+
/** Author login matches the authenticated viewer. */
|
|
49
|
+
owned?: true;
|
|
42
50
|
/** Conservative routing hint; the selected one-PR poll makes the authoritative decision. */
|
|
43
51
|
action: ShepherdAction;
|
|
44
52
|
reasons: string[];
|
|
@@ -89,6 +97,11 @@ export interface PollSummaryResult {
|
|
|
89
97
|
nextAction?: StackNextAction;
|
|
90
98
|
/** Whether every open layer is independently ready and stack ancestry is linear. */
|
|
91
99
|
stackMergeable?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Repository merge methods for stack merge commands. Omitted from the lean
|
|
102
|
+
* stack overview. Empty when the repository enables none.
|
|
103
|
+
*/
|
|
104
|
+
allowedMergeMethods?: import("../config/merge-method.mts").MergeMethod[];
|
|
92
105
|
instructions?: string[];
|
|
93
106
|
apiUsage?: ApiUsage;
|
|
94
107
|
quotaWarning?: GraphqlQuotaWarning;
|