pr-shepherd 0.60.1 → 1.1.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 +7 -5
- package/bin/api.d.mts +35 -24
- package/bin/api.mjs +68 -48
- package/bin/checks/classify.d.mts +1 -1
- package/bin/checks/classify.mjs +4 -4
- package/bin/checks/log-excerpt.mjs +1 -1
- package/bin/cli/arg-flags.mjs +0 -3
- package/bin/cli/clean-handler.d.mts +1 -1
- package/bin/cli/clean-handler.mjs +5 -15
- package/bin/cli/default-poll.d.mts +2 -0
- package/bin/cli/default-poll.mjs +9 -6
- package/bin/cli/formatters.d.mts +2 -3
- package/bin/cli/formatters.mjs +0 -26
- package/bin/cli/handlers.d.mts +3 -3
- package/bin/cli/handlers.mjs +45 -35
- package/bin/cli/help-command-pages.d.mts +13 -158
- package/bin/cli/help-command-pages.mjs +12 -157
- package/bin/cli/help-iterate-poll-pages.d.mts +4 -3
- package/bin/cli/help-iterate-poll-pages.mjs +76 -36
- package/bin/cli/help-log-file-page.d.mts +1 -1
- package/bin/cli/help-log-file-page.mjs +3 -5
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +19 -20
- package/bin/cli/help.d.mts +14 -159
- package/bin/cli/iterate-checks-formatter.mjs +1 -1
- package/bin/cli/iterate-flags.d.mts +2 -4
- package/bin/cli/iterate-flags.mjs +4 -20
- package/bin/cli/iterate-formatter.d.mts +1 -1
- package/bin/cli/iterate-formatter.mjs +6 -6
- package/bin/cli/iterate-instructions.d.mts +1 -1
- package/bin/cli/iterate-instructions.mjs +5 -6
- package/bin/cli/iterate-lean.mjs +32 -40
- package/bin/cli/iterate-verbose.mjs +1 -1
- package/bin/cli/journal-formatter.mjs +0 -3
- package/bin/cli/journal-handler.d.mts +1 -1
- package/bin/cli/journal-handler.mjs +2 -1
- package/bin/cli/mutate-formatter.mjs +0 -9
- package/bin/cli/next-check-format.d.mts +0 -4
- package/bin/cli/next-check-format.mjs +0 -4
- package/bin/cli/poll-handler.d.mts +3 -0
- package/bin/cli/poll-handler.mjs +88 -47
- package/bin/cli/poll-summary-emitter.mjs +2 -2
- package/bin/cli/poll-summary-formatter.mjs +5 -2
- package/bin/cli/poll-targets.d.mts +2 -2
- package/bin/cli/poll-targets.mjs +15 -35
- package/bin/cli/removed-flags.d.mts +3 -0
- package/bin/cli/removed-flags.mjs +24 -0
- package/bin/cli/rule-auto-resolve-lines.d.mts +3 -0
- package/bin/cli/rule-auto-resolve-lines.mjs +12 -0
- package/bin/cli/runner.d.mts +1 -1
- package/bin/cli/runner.mjs +2 -2
- package/bin/cli/stack-layer-view.d.mts +2 -1
- package/bin/cli/stack-overview.d.mts +0 -1
- package/bin/cli/stack-overview.mjs +4 -2
- package/bin/cli/{fix-formatter-extra.d.mts → triage-formatter-extra.d.mts} +1 -2
- package/bin/cli/{fix-formatter-extra.mjs → triage-formatter-extra.mjs} +0 -6
- package/bin/cli/triage-formatter.d.mts +4 -0
- package/bin/cli/{fix-formatter.mjs → triage-formatter.mjs} +31 -42
- package/bin/cli/unknown-flags.d.mts +6 -0
- package/bin/cli/unknown-flags.mjs +29 -0
- package/bin/cli-parser.mjs +41 -62
- package/bin/commands/check-activity.d.mts +8 -0
- package/bin/commands/check-activity.mjs +26 -0
- package/bin/commands/check-annotations.d.mts +1 -1
- package/bin/commands/check-annotations.mjs +3 -2
- package/bin/commands/check-review-budget.d.mts +1 -1
- package/bin/commands/check-review-budget.mjs +1 -1
- package/bin/commands/check-wait-detectors.d.mts +1 -1
- package/bin/commands/check-wait-detectors.mjs +12 -1
- package/bin/commands/check.d.mts +1 -1
- package/bin/commands/check.mjs +13 -8
- package/bin/commands/commit-suggestion-instruction.d.mts +2 -2
- package/bin/commands/commit-suggestion-instruction.mjs +3 -3
- package/bin/commands/iterate/api-usage.mjs +8 -8
- package/bin/commands/iterate/check-instructions.d.mts +4 -4
- package/bin/commands/iterate/check-instructions.mjs +8 -8
- package/bin/commands/iterate/classify.mjs +2 -2
- package/bin/commands/iterate/escalate.mjs +6 -6
- package/bin/commands/iterate/index.mjs +20 -23
- package/bin/commands/iterate/merge-plan.mjs +3 -3
- package/bin/commands/iterate/merge-state.mjs +4 -7
- package/bin/commands/iterate/native-stack-rebase.mjs +1 -1
- package/bin/commands/iterate/render.d.mts +1 -1
- package/bin/commands/iterate/render.mjs +2 -2
- package/bin/commands/iterate/stall.mjs +11 -9
- package/bin/commands/iterate/{fix-loop-instruction.mjs → triage-loop-instruction.mjs} +7 -3
- package/bin/commands/iterate/{fix-code.d.mts → triage.d.mts} +2 -2
- package/bin/commands/iterate/{fix-code.mjs → triage.mjs} +14 -23
- package/bin/commands/iterate/unreported-required.mjs +2 -5
- package/bin/commands/journal/index.d.mts +1 -3
- package/bin/commands/journal/index.mjs +1 -1
- package/bin/commands/mark-files-as-viewed.d.mts +1 -3
- package/bin/commands/mark-files-as-viewed.mjs +1 -1
- package/bin/commands/next-check.d.mts +2 -2
- package/bin/commands/next-check.mjs +4 -2
- package/bin/commands/poll-progress.mjs +1 -1
- package/bin/commands/poll-summary-explicit-instructions.mjs +1 -1
- package/bin/commands/poll-summary-instructions.mjs +7 -7
- package/bin/commands/poll-summary-signature.d.mts +6 -0
- package/bin/commands/poll-summary-signature.mjs +19 -2
- package/bin/commands/poll-summary.mjs +11 -4
- package/bin/commands/poll.d.mts +1 -1
- package/bin/commands/poll.mjs +4 -4
- package/bin/commands/ready-delay.d.mts +1 -0
- package/bin/commands/ready-delay.mjs +8 -3
- package/bin/commands/resolve-mutate.d.mts +1 -1
- package/bin/commands/resolve-mutate.mjs +1 -1
- package/bin/commands/rest-stack-merge-readiness.mjs +1 -1
- package/bin/commands/rule-auto-resolve-format.d.mts +7 -0
- package/bin/commands/rule-auto-resolve-format.mjs +11 -4
- package/bin/commands/rule-auto-resolve.d.mts +14 -2
- package/bin/commands/single-tick.d.mts +5 -0
- package/bin/commands/single-tick.mjs +7 -0
- package/bin/commands/stack-drain.mjs +1 -1
- package/bin/commands/stack-merge-flag.mjs +1 -1
- package/bin/commands/stack-stall.d.mts +2 -2
- package/bin/commands/stack-stall.mjs +2 -2
- package/bin/commands/stack-work.d.mts +2 -2
- package/bin/commands/stack-work.mjs +2 -2
- package/bin/commands/suggestion-patch-item.d.mts +0 -1
- package/bin/commands/suggestion-patch-item.mjs +0 -12
- package/bin/commands/suggestion-patches.d.mts +2 -2
- package/bin/commands/suggestion-patches.mjs +1 -1
- package/bin/commands/tick-instructions.d.mts +9 -0
- package/bin/commands/tick-instructions.mjs +23 -0
- package/bin/commands/tick.d.mts +15 -0
- package/bin/commands/{event-mode.mjs → tick.mjs} +39 -55
- package/bin/comments/minimize-policy.mjs +1 -3
- package/bin/comments/resolve.d.mts +0 -23
- package/bin/comments/resolve.mjs +0 -4
- package/bin/comments/review-output-budget.d.mts +1 -1
- package/bin/comments/review-output-budget.mjs +1 -1
- package/bin/comments/sha-poll.mjs +3 -1
- package/bin/comments/thread-visibility.d.mts +6 -0
- package/bin/comments/thread-visibility.mjs +30 -15
- package/bin/config/default-bot-usernames.d.mts +2 -0
- package/bin/config/default-bot-usernames.mjs +15 -0
- package/bin/config/load.d.mts +4 -138
- package/bin/config/load.mjs +88 -443
- package/bin/config/merge-command-args.mjs +2 -2
- package/bin/config/merge-method.d.mts +1 -2
- package/bin/config/merge-method.mjs +1 -8
- package/bin/config/renames.d.mts +8 -0
- package/bin/config/renames.mjs +87 -0
- package/bin/config/resolve.d.mts +31 -0
- package/bin/config/resolve.mjs +89 -0
- package/bin/config/schema.d.mts +161 -0
- package/bin/config/schema.mjs +168 -0
- package/bin/exit-codes.d.mts +1 -1
- package/bin/exit-codes.mjs +3 -3
- package/bin/github/batch-parse-suites.d.mts +14 -0
- package/bin/github/batch-parse-suites.mjs +4 -1
- package/bin/github/batch-parser-helpers.mjs +3 -0
- package/bin/github/check-annotation-cache.d.mts +5 -0
- package/bin/github/check-annotation-cache.mjs +6 -3
- package/bin/github/fingerprint.mjs +4 -0
- package/bin/github/poll-summary-annotation-probe.d.mts +5 -0
- package/bin/github/poll-summary-annotation-probe.mjs +17 -3
- package/bin/github/poll-summary-annotations.d.mts +14 -0
- package/bin/github/poll-summary-annotations.mjs +75 -0
- package/bin/github/poll-summary-blocking-reviewer.d.mts +2 -0
- package/bin/github/poll-summary-blocking-reviewer.mjs +9 -0
- package/bin/github/poll-summary-check-blockers.mjs +1 -1
- package/bin/github/poll-summary-checks.d.mts +1 -0
- package/bin/github/poll-summary-checks.mjs +20 -2
- package/bin/github/poll-summary-command.mjs +8 -6
- package/bin/github/poll-summary-fingerprint.d.mts +7 -0
- package/bin/github/poll-summary-fingerprint.mjs +7 -0
- package/bin/github/poll-summary-projector.d.mts +3 -1
- package/bin/github/poll-summary-projector.mjs +14 -25
- package/bin/github/poll-summary-queue-removal.d.mts +2 -0
- package/bin/github/poll-summary-queue-removal.mjs +11 -0
- package/bin/github/poll-summary-readiness.mjs +1 -0
- package/bin/github/poll-summary-review-parity.d.mts +24 -0
- package/bin/github/poll-summary-review-parity.mjs +82 -0
- package/bin/github/poll-summary-review.d.mts +11 -2
- package/bin/github/poll-summary-review.mjs +88 -60
- package/bin/github/poll-summary-route.mjs +9 -9
- package/bin/github/poll-summary-row-error.d.mts +16 -0
- package/bin/github/poll-summary-row-error.mjs +74 -0
- package/bin/github/poll-summary-rule-auto-resolve.d.mts +32 -0
- package/bin/github/poll-summary-rule-auto-resolve.mjs +119 -0
- package/bin/github/poll-summary-rule-handoff.d.mts +7 -0
- package/bin/github/poll-summary-rule-handoff.mjs +20 -0
- package/bin/github/poll-summary-rule-items.d.mts +20 -0
- package/bin/github/poll-summary-rule-items.mjs +28 -0
- package/bin/github/poll-summary-unreported.mjs +1 -1
- package/bin/github/poll-summary.mjs +16 -8
- package/bin/github/queries.d.mts +1 -1
- package/bin/github/queries.mjs +1 -1
- package/bin/github/rest-reader-core.mjs +13 -2
- package/bin/github/rest-summary-read.mjs +12 -1
- package/bin/mcp/server.mjs +48 -54
- package/bin/merge-status/derive.mjs +1 -1
- package/bin/state/durable-state.d.mts +1 -3
- package/bin/state/durable-state.mjs +2 -8
- package/bin/state/fix-attempts.d.mts +2 -2
- package/bin/state/fix-attempts.mjs +1 -1
- package/bin/state/merge-request.mjs +1 -1
- package/bin/state/pr-fingerprint.mjs +4 -11
- package/bin/state/seen-comments.d.mts +4 -5
- package/bin/state/seen-comments.mjs +8 -14
- package/bin/state/stack-stall.d.mts +1 -1
- package/bin/state/stack-stall.mjs +1 -1
- package/bin/state/stall-state-store.d.mts +1 -1
- package/bin/state/stall-state-store.mjs +1 -1
- package/bin/types/escalate.d.mts +2 -2
- package/bin/types/github.d.mts +2 -0
- package/bin/types/iterate.d.mts +12 -26
- package/bin/types/merge-queue.d.mts +2 -2
- package/bin/types/next-check.d.mts +1 -1
- package/bin/types/poll-summary.d.mts +26 -6
- package/bin/types/report.d.mts +2 -2
- package/bin/types/suggestion-patch.d.mts +1 -8
- package/package.json +3 -2
- package/plugins/pr-shepherd/.claude-plugin/plugin.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 +6 -6
- package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/{cloud-event-loop.md → event-loop.md} +5 -5
- package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +3 -3
- package/plugins/pr-shepherd/skills/pr-shepherd/references/merge-queue-ejection.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/suggestion-patches.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/references/{fix-code-loop.md → triage-loop.md} +3 -3
- package/plugins/pr-shepherd/skills/reduce-pr-noise/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -2
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/settings.md +11 -11
- package/pr-shepherd.schema.json +361 -0
- package/bin/cli/event-state-scope.d.mts +0 -19
- package/bin/cli/event-state-scope.mjs +0 -60
- package/bin/cli/fix-formatter.d.mts +0 -4
- package/bin/commands/commit-suggestion.d.mts +0 -8
- package/bin/commands/commit-suggestion.mjs +0 -26
- package/bin/commands/event-instructions.d.mts +0 -11
- package/bin/commands/event-instructions.mjs +0 -27
- package/bin/commands/event-mode.d.mts +0 -14
- package/bin/commands/poll-mode.d.mts +0 -8
- package/bin/commands/poll-mode.mjs +0 -14
- package/bin/config.json +0 -68
- package/bin/types/protected-run.d.mts +0 -6
- package/bin/types/protected-run.mjs +0 -1
- /package/bin/commands/iterate/{fix-loop-instruction.d.mts → triage-loop-instruction.d.mts} +0 -0
|
@@ -8,7 +8,7 @@ import { buildWaitLog, buildTerminalCancelResult, blockedCancelNote } from "./he
|
|
|
8
8
|
import { classifyReviewSummaries } from "./classify.mjs";
|
|
9
9
|
import { applyStallGuard } from "./stall.mjs";
|
|
10
10
|
import { clearStallState } from "../../state/iterate-stall.mjs";
|
|
11
|
-
import {
|
|
11
|
+
import { handleTriage } from "./triage.mjs";
|
|
12
12
|
import { normalizeBotUsernames } from "../../comments/authors.mjs";
|
|
13
13
|
import { autoMinimizeComments } from "../../comments/resolve.mjs";
|
|
14
14
|
import { hasCheckDrivenActionableWork } from "../check-annotations.mjs";
|
|
@@ -60,24 +60,21 @@ async function attachEditedComments(result, tick, opts) {
|
|
|
60
60
|
}
|
|
61
61
|
async function runIterateCore(opts, tick) {
|
|
62
62
|
const config = loadConfig();
|
|
63
|
-
const botUsernames = normalizeBotUsernames(config
|
|
64
|
-
const readyDelaySeconds = opts.readyDelaySeconds ?? config.
|
|
65
|
-
const stallTimeoutSeconds = opts.stallTimeoutSeconds ?? config.iterate
|
|
63
|
+
const botUsernames = normalizeBotUsernames(config["bot-usernames"]);
|
|
64
|
+
const readyDelaySeconds = opts.readyDelaySeconds ?? config.iterate["ready-delay-minutes"] * 60;
|
|
65
|
+
const stallTimeoutSeconds = opts.stallTimeoutSeconds ?? config.iterate["stall-timeout-minutes"] * 60;
|
|
66
66
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
67
67
|
if (prNumber === null) {
|
|
68
68
|
throw new ShepherdError("No open PR found for current branch. Pass a PR number explicitly.", EXIT.UNAVAILABLE);
|
|
69
69
|
}
|
|
70
|
-
const neverCancelRuns = opts.neverCancelRuns ?? config.actions.neverCancelRuns;
|
|
71
70
|
const checkContext = createCheckExecutionContext(readyDelaySeconds);
|
|
72
71
|
const report = await runCheck({
|
|
73
72
|
...opts,
|
|
74
73
|
prNumber,
|
|
75
|
-
// Outdated Shepherd-visible threads are always resolved by iterate.
|
|
76
|
-
// old actions.autoResolveOutdated switch is retained only as an ignored
|
|
77
|
-
// compatibility key in the config loader.
|
|
74
|
+
// Outdated Shepherd-visible threads are always resolved by iterate.
|
|
78
75
|
autoResolve: true,
|
|
79
|
-
autoMinimizeSuppressed: config.actions
|
|
80
|
-
reviewOutputMaxChars: config.iterate
|
|
76
|
+
autoMinimizeSuppressed: config.actions["auto-minimize-suppressed"],
|
|
77
|
+
reviewOutputMaxChars: config.iterate["review-output-max-chars"],
|
|
81
78
|
}, checkContext);
|
|
82
79
|
tick.report = report;
|
|
83
80
|
const [repoOwner, repoName] = report.repo.split("/");
|
|
@@ -107,7 +104,7 @@ async function runIterateCore(opts, tick) {
|
|
|
107
104
|
firstLook: report.firstLookSummaries,
|
|
108
105
|
seen: report.reviewSummaries,
|
|
109
106
|
edited: report.editedSummaries,
|
|
110
|
-
}, report.approvedReviews, config.iterate
|
|
107
|
+
}, report.approvedReviews, config.iterate["minimize-approvals"], config.iterate["minimize-comments"], botUsernames, [
|
|
111
108
|
...report.threads.actionable,
|
|
112
109
|
...report.threads.resolutionOnly,
|
|
113
110
|
...(report.heldThreadReviewIds ?? []).map((reviewId) => ({ reviewId })),
|
|
@@ -128,7 +125,7 @@ async function runIterateCore(opts, tick) {
|
|
|
128
125
|
}
|
|
129
126
|
// A first look at an already-minimized comment is only an acknowledgment. Edited PR comments
|
|
130
127
|
// (report.comments.edited) are neither: they never block readiness or restart the ready delay.
|
|
131
|
-
// Review items held back by iterate.
|
|
128
|
+
// Review items held back by iterate.review-output-max-chars keep the PR out of ready/merge: a
|
|
132
129
|
// capped tick that shows only first-look comments still counts, since held work follows.
|
|
133
130
|
const hasReadinessWork = (report.reviewItems !== undefined && report.comments.firstLook.length > 0) ||
|
|
134
131
|
report.threads.actionable.length > 0 ||
|
|
@@ -142,7 +139,7 @@ async function runIterateCore(opts, tick) {
|
|
|
142
139
|
reviewSummaryIds.length > 0 ||
|
|
143
140
|
firstLookSummaries.length > 0 ||
|
|
144
141
|
editedSummaries.length > 0 ||
|
|
145
|
-
(config.iterate
|
|
142
|
+
(config.iterate["minimize-approvals"] && surfacedApprovals.length > 0);
|
|
146
143
|
const hasActionableWork = hasReadinessWork || report.comments.firstLook.length > 0;
|
|
147
144
|
const activeMerge = Boolean(opts.merge &&
|
|
148
145
|
(report.mergeQueue?.inQueue ||
|
|
@@ -183,12 +180,12 @@ async function runIterateCore(opts, tick) {
|
|
|
183
180
|
const checkDrivenActionableWork = hasCheckDrivenActionableWork(reportForWork.checks, report.mergeStatus.status);
|
|
184
181
|
const deferWhileQueued = opts.merge === true &&
|
|
185
182
|
report.mergeQueue?.inQueue === true &&
|
|
186
|
-
config.actions
|
|
183
|
+
config.actions["work-while-queued"] !== true;
|
|
187
184
|
if (hasActionableWork && !(deferWhileQueued && !checkDrivenActionableWork)) {
|
|
188
|
-
return
|
|
185
|
+
return handleTriage({
|
|
189
186
|
base,
|
|
190
187
|
report: reportForWork,
|
|
191
|
-
opts: { ...opts, prNumber
|
|
188
|
+
opts: { ...opts, prNumber },
|
|
192
189
|
headSha,
|
|
193
190
|
stallKey,
|
|
194
191
|
prNumber,
|
|
@@ -208,10 +205,10 @@ async function runIterateCore(opts, tick) {
|
|
|
208
205
|
});
|
|
209
206
|
}
|
|
210
207
|
if (unreportedRepair.length > 0 && staleAncestry) {
|
|
211
|
-
return
|
|
208
|
+
return handleTriage({
|
|
212
209
|
base,
|
|
213
210
|
report: reportForWork,
|
|
214
|
-
opts: { ...opts, prNumber
|
|
211
|
+
opts: { ...opts, prNumber },
|
|
215
212
|
headSha,
|
|
216
213
|
stallKey,
|
|
217
214
|
prNumber,
|
|
@@ -241,15 +238,15 @@ async function runIterateCore(opts, tick) {
|
|
|
241
238
|
firstLookSummaries,
|
|
242
239
|
editedSummaries,
|
|
243
240
|
surfacedApprovals,
|
|
244
|
-
minimizeApprovals: config.iterate
|
|
241
|
+
minimizeApprovals: config.iterate["minimize-approvals"],
|
|
245
242
|
});
|
|
246
243
|
if (mergeStateResult)
|
|
247
244
|
return annotateBlockedWait(mergeStateResult, blockerGate);
|
|
248
245
|
if (staleAncestry) {
|
|
249
|
-
return
|
|
246
|
+
return handleTriage({
|
|
250
247
|
base,
|
|
251
248
|
report: reportForWork,
|
|
252
|
-
opts: { ...opts, prNumber
|
|
249
|
+
opts: { ...opts, prNumber },
|
|
253
250
|
headSha,
|
|
254
251
|
stallKey,
|
|
255
252
|
prNumber,
|
|
@@ -287,7 +284,7 @@ async function runIterateCore(opts, tick) {
|
|
|
287
284
|
escalate: { ...escalation, humanMessage: buildEscalateHumanMessage(escalation, report.pr) },
|
|
288
285
|
};
|
|
289
286
|
}
|
|
290
|
-
const autoMarkReady = !opts.noAutoMarkReady && config.
|
|
287
|
+
const autoMarkReady = !opts.noAutoMarkReady && config["mark-ready"].auto;
|
|
291
288
|
if (canMarkReady && !autoMarkReady) {
|
|
292
289
|
await clearReadyDelay(report.pr, repoOwner, repoName);
|
|
293
290
|
await clearStallState(stallKey);
|
|
@@ -307,7 +304,7 @@ async function runIterateCore(opts, tick) {
|
|
|
307
304
|
const receipt = receiptCurrent
|
|
308
305
|
? "written"
|
|
309
306
|
: await recordReadyReceipt(receiptKey, report, checkContext);
|
|
310
|
-
// Aggregate
|
|
307
|
+
// Aggregate `stack` routing trusts only a layer's receipt, so an unwritten
|
|
311
308
|
// one keeps the elapsed marker and retries next tick. A one-PR receipt
|
|
312
309
|
// only lets a rerun skip the wait, so its failure never changes the action.
|
|
313
310
|
// A layer GitHub reports BLOCKED gets no receipt, so aggregate routing
|
|
@@ -7,7 +7,7 @@ import { getGithubTransport } from "../../github/transport.mjs";
|
|
|
7
7
|
const ENQUEUE_MUTATION = "mutation EnqueuePullRequest($pullRequestId: ID!, $expectedHeadOid: GitObjectID!) { enqueuePullRequest(input: { pullRequestId: $pullRequestId, expectedHeadOid: $expectedHeadOid }) { mergeQueueEntry { id } } }";
|
|
8
8
|
export function buildMergeCommandPlan(input) {
|
|
9
9
|
if (input.transport === "rest" || getGithubTransport() === "rest") {
|
|
10
|
-
const configured = configuredMergeMethod(loadConfig().merge
|
|
10
|
+
const configured = configuredMergeMethod(loadConfig().merge);
|
|
11
11
|
if (!input.queue && input.queueKnown !== true && configured)
|
|
12
12
|
return {
|
|
13
13
|
unavailable: "REST cannot verify merge-queue policy and cannot apply a configured direct merge method to an automatic merge request. Refresh branch policy before merging.",
|
|
@@ -70,10 +70,10 @@ export function buildMergeCommandPlan(input) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
const mergeConfig = loadConfig().merge;
|
|
73
|
-
const configuredArgs = mergeConfig
|
|
73
|
+
const configuredArgs = mergeConfig["command-args"];
|
|
74
74
|
const decision = chooseMergeMethod({
|
|
75
75
|
allowed: input.allowedMergeMethods,
|
|
76
|
-
configured: configuredMergeMethod(mergeConfig
|
|
76
|
+
configured: configuredMergeMethod(mergeConfig),
|
|
77
77
|
fallback: "merge",
|
|
78
78
|
});
|
|
79
79
|
if ("unavailable" in decision)
|
|
@@ -6,14 +6,14 @@ import { buildPrShepherdCommand } from "../../cli/runner.mjs";
|
|
|
6
6
|
import { inlineCode } from "../../util/markdown.mjs";
|
|
7
7
|
/**
|
|
8
8
|
* A one-PR poll cannot prove the layers below it merged or that its PR number is a safe merge
|
|
9
|
-
* selector. Route it through the aggregate stack
|
|
9
|
+
* selector. Route it through the aggregate stack command, which merges one bottom layer at a time.
|
|
10
10
|
*/
|
|
11
11
|
function buildStackedRouteResult(base, report, stack) {
|
|
12
12
|
const prUrl = formatPrUrl(report.repo, report.pr);
|
|
13
13
|
return {
|
|
14
14
|
...base,
|
|
15
|
-
action: "
|
|
16
|
-
|
|
15
|
+
action: "triage",
|
|
16
|
+
triage: {
|
|
17
17
|
threads: [],
|
|
18
18
|
resolutionOnlyThreads: [],
|
|
19
19
|
actionableComments: [],
|
|
@@ -31,14 +31,11 @@ function buildStackedRouteResult(base, report, stack) {
|
|
|
31
31
|
},
|
|
32
32
|
instructions: [
|
|
33
33
|
`PR #${report.pr} is layer ${stack.position} of ${stack.size} in native stack #${stack.number}; do not run \`gh pr merge\` for this layer.`,
|
|
34
|
-
`Run ${inlineCode(buildPrShepherdCommand(["
|
|
34
|
+
`Run ${inlineCode(buildPrShepherdCommand(["stack", prUrl, "--until-terminal", "--merge"]).text)} to reconcile the stack and run each bottom-layer merge command it emits.`,
|
|
35
35
|
],
|
|
36
|
-
inProgressRunIds: [],
|
|
37
|
-
protectedRuns: [],
|
|
38
36
|
firstLookThreads: [],
|
|
39
37
|
firstLookComments: [],
|
|
40
38
|
},
|
|
41
|
-
cancelled: [],
|
|
42
39
|
};
|
|
43
40
|
}
|
|
44
41
|
export function buildReadyMergeOutcome(enabled, readyElapsed, base, report) {
|
|
@@ -15,7 +15,7 @@ export function buildNativeStackRebaseInstruction(repo, stackNumber, start) {
|
|
|
15
15
|
: "trunk" in start
|
|
16
16
|
? `trunk \`${start.trunk}\``
|
|
17
17
|
: `bottom open layer PR #${start.bottomPr}`;
|
|
18
|
-
return `Use the repository's stack-update procedure for native stack #${stackNumber} in \`${repo}\`, starting at ${boundary} and preserving the ordered parent boundaries of every affected upper layer. Read membership with REST-backed Shepherd
|
|
18
|
+
return `Use the repository's stack-update procedure for native stack #${stackNumber} in \`${repo}\`, starting at ${boundary} and preserving the ordered parent boundaries of every affected upper layer. Read membership with REST-backed Shepherd \`stack\` output and update and push affected branches using the caller's git workflow. ${playbookPointer("Branch update")}`;
|
|
19
19
|
}
|
|
20
20
|
const [checkout, command] = "parentBranch" in start
|
|
21
21
|
? [
|
|
@@ -7,7 +7,7 @@ import type { InstructionStyle } from "../../config/load.mts";
|
|
|
7
7
|
* if it is not. `apply review` reads that HEAD itself with a read-only `git rev-parse HEAD`.
|
|
8
8
|
*/
|
|
9
9
|
export declare function renderResolveCommand(rc: ResolveCommand): string;
|
|
10
|
-
export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prReference: string | number,
|
|
10
|
+
export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prReference: string | number, firstLookThreads?: FirstLookThread[], firstLookComments?: FirstLookComment[], firstLookSummaries?: Review[], editedSummaries?: Review[], resolutionOnlyThreads?: ReviewThread[], resolveOnlyCommand?: ResolveCommand, behindBaseHint?: string, // iterate.behind-base-hint — see buildBehindBaseHintInstruction
|
|
11
11
|
isBehind?: boolean, _viewerCanUpdate?: boolean, hasExhaustedWorkflowRerun?: boolean, stackRebase?: string, // native stack layers rebase with gh-stack, not branch by branch
|
|
12
12
|
queueEjection?: QueueEjectionRecovery, // current merge-queue removal with failing queue checks
|
|
13
13
|
instructionStyle?: InstructionStyle): string[];
|
|
@@ -5,7 +5,7 @@ import { buildCommitSuggestionInstruction } from "../commit-suggestion-instructi
|
|
|
5
5
|
import { partitionFixThreads } from "./fix-instruction-threads.mjs";
|
|
6
6
|
import { buildBranchPushInstruction, buildConflictInstruction } from "./native-stack-rebase.mjs";
|
|
7
7
|
import { queueEjectionSteps } from "./queue-recovery-instructions.mjs";
|
|
8
|
-
import { buildPushSteps } from "./
|
|
8
|
+
import { buildPushSteps } from "./triage-loop-instruction.mjs";
|
|
9
9
|
/**
|
|
10
10
|
* Render a resolve command as a shell snippet. Appends `--require-sha HEAD` when set: run after
|
|
11
11
|
* the push step, the local HEAD is the pushed PR head, and `apply review` rejects the mutations
|
|
@@ -16,7 +16,7 @@ export function renderResolveCommand(rc) {
|
|
|
16
16
|
return rc.requiresHeadSha ? `${command} ${REQUIRE_HEAD_SHA}` : command;
|
|
17
17
|
}
|
|
18
18
|
const REQUIRE_HEAD_SHA = "--require-sha HEAD";
|
|
19
|
-
export function buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseBranch, resolveCommand, hasConflicts, prReference,
|
|
19
|
+
export function buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseBranch, resolveCommand, hasConflicts, prReference, firstLookThreads = [], firstLookComments = [], firstLookSummaries = [], editedSummaries = [], resolutionOnlyThreads = [], resolveOnlyCommand, behindBaseHint = "", // iterate.behind-base-hint — see buildBehindBaseHintInstruction
|
|
20
20
|
isBehind = false, _viewerCanUpdate = false, hasExhaustedWorkflowRerun = false, stackRebase, // native stack layers rebase with gh-stack, not branch by branch
|
|
21
21
|
queueEjection, // current merge-queue removal with failing queue checks
|
|
22
22
|
instructionStyle = "inline") {
|
|
@@ -5,25 +5,27 @@ import { buildEscalateSuggestion, buildEscalateHumanMessage, formatDurationAppro
|
|
|
5
5
|
import { checksWithActionableAnnotations } from "../check-annotations.mjs";
|
|
6
6
|
import { formatPrUrl } from "../../pr-reference.mjs";
|
|
7
7
|
function pendingReviewCommandsFromResult(result) {
|
|
8
|
-
if (result.action !== "
|
|
8
|
+
if (result.action !== "triage")
|
|
9
9
|
return undefined;
|
|
10
10
|
const pending = {
|
|
11
|
-
...(result.
|
|
12
|
-
resolveOnlyCommand: result.
|
|
11
|
+
...(result.triage.resolveOnlyCommand?.hasMutations && {
|
|
12
|
+
resolveOnlyCommand: result.triage.resolveOnlyCommand,
|
|
13
|
+
}),
|
|
14
|
+
...(result.triage.resolveCommand.hasMutations && {
|
|
15
|
+
resolveCommand: result.triage.resolveCommand,
|
|
13
16
|
}),
|
|
14
|
-
...(result.fix.resolveCommand.hasMutations && { resolveCommand: result.fix.resolveCommand }),
|
|
15
17
|
};
|
|
16
18
|
return Object.keys(pending).length > 0 ? pending : undefined;
|
|
17
19
|
}
|
|
18
20
|
function surfacedSummariesFromResult(result) {
|
|
19
|
-
if (result.action !== "
|
|
21
|
+
if (result.action !== "triage")
|
|
20
22
|
return {};
|
|
21
23
|
return {
|
|
22
|
-
...(result.
|
|
23
|
-
firstLookSummaries: result.
|
|
24
|
+
...(result.triage.firstLookSummaries.length > 0 && {
|
|
25
|
+
firstLookSummaries: result.triage.firstLookSummaries,
|
|
24
26
|
}),
|
|
25
|
-
...(result.
|
|
26
|
-
editedSummaries: result.
|
|
27
|
+
...(result.triage.editedSummaries.length > 0 && {
|
|
28
|
+
editedSummaries: result.triage.editedSummaries,
|
|
27
29
|
}),
|
|
28
30
|
};
|
|
29
31
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderShellCommand } from "../../cli/runner.mjs";
|
|
2
2
|
import { loadConfig } from "../../config/load.mjs";
|
|
3
3
|
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
4
|
-
const
|
|
4
|
+
const TRIAGE_LOOP_PLAYBOOK = "Triage loop";
|
|
5
5
|
/** The generic push stays separate from conflict and queue-recovery push instructions. */
|
|
6
6
|
export function buildPushSteps(style, genericPush) {
|
|
7
7
|
if (!genericPush)
|
|
@@ -9,6 +9,10 @@ export function buildPushSteps(style, genericPush) {
|
|
|
9
9
|
const push = "Commit and push any changes.";
|
|
10
10
|
if (style === "inline")
|
|
11
11
|
return [push];
|
|
12
|
-
const cli = renderShellCommand([
|
|
13
|
-
|
|
12
|
+
const cli = renderShellCommand([
|
|
13
|
+
...loadConfig()["cli-command"],
|
|
14
|
+
"playbook",
|
|
15
|
+
TRIAGE_LOOP_PLAYBOOK,
|
|
16
|
+
]);
|
|
17
|
+
return [`${push} ${playbookPointer(TRIAGE_LOOP_PLAYBOOK)} CLI: \`${cli}\`.`];
|
|
14
18
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IterateCommandOptions, IterateResult, IterateResultBase, Review, ShepherdReport } from "../../types.mts";
|
|
2
2
|
import type { NormalizedBotUsernames } from "../../comments/authors.mts";
|
|
3
|
-
interface
|
|
3
|
+
interface HandleTriageContext {
|
|
4
4
|
base: IterateResultBase;
|
|
5
5
|
report: ShepherdReport;
|
|
6
6
|
opts: IterateCommandOptions;
|
|
@@ -25,5 +25,5 @@ interface HandleFixCodeContext {
|
|
|
25
25
|
/** Verified stack-repair guidance, when ancestry is stale. */
|
|
26
26
|
repairInstructions?: string[];
|
|
27
27
|
}
|
|
28
|
-
export declare function
|
|
28
|
+
export declare function handleTriage(ctx: HandleTriageContext): Promise<IterateResult>;
|
|
29
29
|
export {};
|
|
@@ -77,22 +77,22 @@ function pendingReviewCommands(resolveCommand, resolveOnlyCommand) {
|
|
|
77
77
|
};
|
|
78
78
|
return Object.keys(pending).length > 0 ? pending : undefined;
|
|
79
79
|
}
|
|
80
|
-
export async function
|
|
81
|
-
const result = await
|
|
80
|
+
export async function handleTriage(ctx) {
|
|
81
|
+
const result = await handleTriageTick(ctx);
|
|
82
82
|
// Every escalation from a capped tick carries the rollup: the reader sees a subset.
|
|
83
83
|
const { reviewItems } = ctx.report;
|
|
84
84
|
if (result.action !== "escalate" || reviewItems === undefined)
|
|
85
85
|
return result;
|
|
86
86
|
return { ...result, escalate: { ...result.escalate, reviewItems } };
|
|
87
87
|
}
|
|
88
|
-
async function
|
|
88
|
+
async function handleTriageTick(ctx) {
|
|
89
89
|
const { base, report, opts, headSha, stallKey, prNumber, stallTimeoutSeconds, repoOwner, repoName, reviewSummaryIds, firstLookSummaries, editedSummaries, surfacedApprovals, botUsernames, releasedCheckNames = EMPTY_RELEASED, ruleAutoResolveThreadIds, repairInstructions, } = ctx;
|
|
90
90
|
const prReference = formatPrUrl(report.repo, prNumber);
|
|
91
91
|
const failingChecks = report.checks.failing;
|
|
92
92
|
const annotatedExtra = checksWithActionableAnnotations(report).filter((c) => c.category !== "failing");
|
|
93
93
|
const allThreads = [...report.threads.actionable, ...report.threads.resolutionOnly];
|
|
94
94
|
const ruleAutoResolveIds = new Set(ruleAutoResolveThreadIds ?? []);
|
|
95
|
-
const resolveOtherHumanThreads = loadConfig().iterate
|
|
95
|
+
const resolveOtherHumanThreads = loadConfig().iterate["resolve-other-human-threads"] ?? "none";
|
|
96
96
|
const routedThreadMutations = buildThreadMutationRouting(allThreads, botUsernames, [...ruleAutoResolveIds], resolveOtherHumanThreads);
|
|
97
97
|
const replyIdSet = new Set(routedThreadMutations.replyThreadIds);
|
|
98
98
|
const resolveIdSet = new Set(routedThreadMutations.resolveThreadIds);
|
|
@@ -107,12 +107,11 @@ async function handleFixCodeTick(ctx) {
|
|
|
107
107
|
((thread.path !== null && thread.line !== null) ||
|
|
108
108
|
threadHasAuthorizedMutation(thread, replyIdSet, resolveIdSet)));
|
|
109
109
|
const retryableActionableThreads = mutationActionableThreads.filter((thread) => thread.path !== null && thread.line !== null);
|
|
110
|
-
const protectedRuns = [];
|
|
111
110
|
const stored = await readFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber });
|
|
112
111
|
await aliasThreadFixAttempts({ owner: repoOwner, name: repoName }, prNumber, retryableActionableThreads.map(({ id }) => id), stored);
|
|
113
|
-
const
|
|
112
|
+
const countTriageAttempt = opts.persistSeen !== false;
|
|
114
113
|
const priorThreadAttempts = previousFixAttempts(stored, retryableActionableThreads);
|
|
115
|
-
const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, retryableActionableThreads,
|
|
114
|
+
const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, retryableActionableThreads, countTriageAttempt);
|
|
116
115
|
const resolutionOnlyThreadsForWork = report.threads.resolutionOnly.filter((thread) => !skippedThreadIds.has(thread.id) &&
|
|
117
116
|
((thread.path !== null && thread.line !== null) ||
|
|
118
117
|
threadHasAuthorizedMutation(thread, replyIdSet, resolveIdSet)));
|
|
@@ -126,7 +125,7 @@ async function handleFixCodeTick(ctx) {
|
|
|
126
125
|
...(report.comments.minimizeIds ?? report.comments.actionable.map((comment) => comment.id)),
|
|
127
126
|
...reviewSummaryIds,
|
|
128
127
|
], changesRequestedReviewsForWork, checks, prReference, botUsernames, ruleAutoResolveThreadIds, report.viewerAuthorization, allThreads, resolveOtherHumanThreads);
|
|
129
|
-
const escalateTriggers =
|
|
128
|
+
const escalateTriggers = countTriageAttempt
|
|
130
129
|
? checkEscalateTriggers(retryableActionableThreads, priorThreadAttempts)
|
|
131
130
|
: { triggers: [], thrashHistory: undefined };
|
|
132
131
|
if (escalateTriggers.triggers.length > 0) {
|
|
@@ -161,7 +160,6 @@ async function handleFixCodeTick(ctx) {
|
|
|
161
160
|
// it does not confirm the credential executing `gh` has that scope; an unauthorized rerun
|
|
162
161
|
// simply fails when the agent runs it, the same residual risk as every other CLI-recommended
|
|
163
162
|
// git/gh mutation in this codebase.
|
|
164
|
-
const cancelled = [];
|
|
165
163
|
const baseLookup = validateBaseBranch(report.baseBranch);
|
|
166
164
|
const threads = report.threads.actionable.map(toAgentThread);
|
|
167
165
|
const resolutionOnlyThreads = report.threads.resolutionOnly;
|
|
@@ -204,11 +202,7 @@ async function handleFixCodeTick(ctx) {
|
|
|
204
202
|
const { changesRequestedReviews } = report;
|
|
205
203
|
const hasConflicts = report.mergeStatus.status === "CONFLICTS";
|
|
206
204
|
const isBehind = report.mergeStatus.status === "BEHIND";
|
|
207
|
-
const { behindBaseHint, instructions: configuredInstructions } = loadConfig().iterate;
|
|
208
|
-
// Only surface in-progress runs when a push is plausible — resolution-only and
|
|
209
|
-
// summary-only iterations have no path to a push, so listing runs would prompt
|
|
210
|
-
// unnecessary cancellation.
|
|
211
|
-
const inProgressRunIds = [];
|
|
205
|
+
const { "behind-base-hint": behindBaseHint, instructions: configuredInstructions } = loadConfig().iterate;
|
|
212
206
|
const commentMinimizeIds = report.comments.minimizeIds ?? actionableComments.map((c) => c.id);
|
|
213
207
|
const belongsToActiveWorkflowRun = (check) => check.runId !== null && inProgressWorkflowRunIds.has(check.runId);
|
|
214
208
|
const manualFollowUpChecks = failingAgentChecks.filter((check) => !releasedCheckNames.has(check.name) &&
|
|
@@ -265,7 +259,7 @@ async function handleFixCodeTick(ctx) {
|
|
|
265
259
|
};
|
|
266
260
|
}
|
|
267
261
|
// Push access to the PR head branch is a usage precondition. Build review mutations for
|
|
268
|
-
// conflict ticks normally so the caller can push and complete the same
|
|
262
|
+
// conflict ticks normally so the caller can push and complete the same triage cycle.
|
|
269
263
|
const { resolveCommand, resolveOnlyCommand } = buildReviewCommands(failingAgentChecks);
|
|
270
264
|
// Safety: if the base branch is unknown, escalate when a push is plausible — the agent
|
|
271
265
|
// would need the correct base to rebase safely. This is a conservative guard, not a
|
|
@@ -346,7 +340,7 @@ async function handleFixCodeTick(ctx) {
|
|
|
346
340
|
const stackRebase = hasConflicts || (isBehind && exhaustedAttempts.length > 0) || queueEjection
|
|
347
341
|
? buildNativeStackLayerRebase(report.repo, { number: prNumber, baseBranch: baseLookup.branch }, stack, trunkConflict, report.stackBottomPr)
|
|
348
342
|
: undefined;
|
|
349
|
-
const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference,
|
|
343
|
+
const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready"), exhaustedAttempts.length > 0, stackRebase, queueEjection, opts.instructions ?? configuredInstructions);
|
|
350
344
|
if (recovery && "unavailable" in recovery)
|
|
351
345
|
instructions.unshift(recovery.unavailable);
|
|
352
346
|
if (acknowledgmentRecovery && "unavailable" in acknowledgmentRecovery)
|
|
@@ -391,8 +385,8 @@ async function handleFixCodeTick(ctx) {
|
|
|
391
385
|
baseBranch: baseLookup.branch,
|
|
392
386
|
...(mergedBasePullRequests.length > 0 && { mergedBasePullRequests }),
|
|
393
387
|
...(trunkConflict && { stackTrunkConflict: trunkConflict.trunk }),
|
|
394
|
-
action: "
|
|
395
|
-
|
|
388
|
+
action: "triage",
|
|
389
|
+
triage: {
|
|
396
390
|
threads,
|
|
397
391
|
resolutionOnlyThreads,
|
|
398
392
|
actionableComments,
|
|
@@ -409,16 +403,13 @@ async function handleFixCodeTick(ctx) {
|
|
|
409
403
|
instructions,
|
|
410
404
|
firstLookThreads,
|
|
411
405
|
firstLookComments,
|
|
412
|
-
inProgressRunIds,
|
|
413
|
-
protectedRuns,
|
|
414
406
|
...(report.reviewItems && { reviewItems: report.reviewItems }),
|
|
415
407
|
},
|
|
416
|
-
cancelled,
|
|
417
408
|
};
|
|
418
409
|
const result = await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, prospectiveResult, report, reviewSummaryIds);
|
|
419
|
-
if (result.action === "
|
|
410
|
+
if (result.action === "triage" && opts.persistSeen !== false) {
|
|
420
411
|
await writeFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber }, { headSha, threadAttempts, threadBodyHashes });
|
|
421
|
-
await Promise.allSettled(result.
|
|
412
|
+
await Promise.allSettled(result.triage.checks.flatMap((ch) => (ch.annotations ?? []).map((a) => markSeen(stallKey, a.id, annotationMarkerBody(a)))));
|
|
422
413
|
}
|
|
423
414
|
return result;
|
|
424
415
|
}
|
|
@@ -84,8 +84,8 @@ export function buildUnreportedFixResult(base, report, instructions) {
|
|
|
84
84
|
const prUrl = formatPrUrl(report.repo, report.pr);
|
|
85
85
|
return {
|
|
86
86
|
...base,
|
|
87
|
-
action: "
|
|
88
|
-
|
|
87
|
+
action: "triage",
|
|
88
|
+
triage: {
|
|
89
89
|
threads: [],
|
|
90
90
|
resolutionOnlyThreads: [],
|
|
91
91
|
actionableComments: [],
|
|
@@ -102,12 +102,9 @@ export function buildUnreportedFixResult(base, report, instructions) {
|
|
|
102
102
|
hasMutations: false,
|
|
103
103
|
},
|
|
104
104
|
instructions: [...instructions, buildFixCompletionInstruction()],
|
|
105
|
-
inProgressRunIds: [],
|
|
106
|
-
protectedRuns: [],
|
|
107
105
|
firstLookThreads: [],
|
|
108
106
|
firstLookComments: [],
|
|
109
107
|
},
|
|
110
|
-
cancelled: [],
|
|
111
108
|
};
|
|
112
109
|
}
|
|
113
110
|
function stackRebase(report) {
|
|
@@ -13,8 +13,6 @@ export interface JournalResult {
|
|
|
13
13
|
sectionExisted: boolean;
|
|
14
14
|
dryRun: boolean;
|
|
15
15
|
previewBody?: string;
|
|
16
|
-
/** @deprecated Explicit journal requests now rely on GitHub's mutation response. */
|
|
17
|
-
authorizationSkipped?: "denied-or-unverifiable";
|
|
18
16
|
}
|
|
19
|
-
/**
|
|
17
|
+
/** Implementation for `apply journal`. */
|
|
20
18
|
export declare function runJournal(opts: RunJournalOptions): Promise<JournalResult>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getRepoInfo, getPullRequestBody, updatePullRequestBody, getCurrentPrNumber, } from "../../github/client.mjs";
|
|
2
2
|
import { validateJournalItem, appendJournalItem } from "./transform.mjs";
|
|
3
|
-
/**
|
|
3
|
+
/** Implementation for `apply journal`. */
|
|
4
4
|
export async function runJournal(opts) {
|
|
5
5
|
const validation = validateJournalItem(opts.rawItem);
|
|
6
6
|
if (!validation.ok) {
|
|
@@ -21,8 +21,6 @@ export interface MarkFilesAsViewedResult {
|
|
|
21
21
|
errors: string[];
|
|
22
22
|
rateLimit?: ResolveRateLimitStop;
|
|
23
23
|
unmarkedPaths?: string[];
|
|
24
|
-
/** @deprecated Explicit file-view requests are attempted; GitHub authorizes the mutation. */
|
|
25
|
-
authorizationSkipped?: "unverifiable";
|
|
26
24
|
}
|
|
27
|
-
/**
|
|
25
|
+
/** Implementation for `apply files`. */
|
|
28
26
|
export declare function runMarkFilesAsViewed(opts: MarkFilesAsViewedOptions): Promise<MarkFilesAsViewedResult>;
|
|
@@ -34,7 +34,7 @@ const FILES_QUERY = `query PullRequestFiles($owner: String!, $repo: String!, $pr
|
|
|
34
34
|
const TEST_FILE_RE = /(^|\/)(tests?|__tests__|spec)(\/|$)|\.(test|spec)\.[cm]?[jt]sx?$|_tests?\.rs$|(^|\/)tests?\.rs$/i;
|
|
35
35
|
// Keep mutation batches small so rate-limit stops leave an ordered pending list.
|
|
36
36
|
const MARK_FILES_CHUNK_SIZE = 10;
|
|
37
|
-
/**
|
|
37
|
+
/** Implementation for `apply files`. */
|
|
38
38
|
export async function runMarkFilesAsViewed(opts) {
|
|
39
39
|
return githubOperation("markFileAsViewed", () => runGraphqlMarkFilesAsViewed(opts), async () => {
|
|
40
40
|
throw new UnsupportedRestOperationError("markFileAsViewed");
|
|
@@ -8,10 +8,10 @@ export interface NextCheckCandidate {
|
|
|
8
8
|
seconds: number;
|
|
9
9
|
}
|
|
10
10
|
/** The earliest candidate as a `NextCheck`, with `at` rounded up to a whole minute. */
|
|
11
|
-
export declare function earliestNextCheck(candidates: NextCheckCandidate[], nowMs?: number): NextCheck | undefined;
|
|
11
|
+
export declare function earliestNextCheck(candidates: NextCheckCandidate[], nowMs?: number, minimumSeconds?: number): NextCheck | undefined;
|
|
12
12
|
interface NextCheckFacts {
|
|
13
13
|
/** The action Shepherd returned for this tick. */
|
|
14
|
-
action: "wait" | "ready" | "mark_ready" | "
|
|
14
|
+
action: "wait" | "ready" | "mark_ready" | "triage" | "merge" | "cancel" | "escalate";
|
|
15
15
|
/** Ready-delay seconds left; only meaningful for `ready`. */
|
|
16
16
|
remainingSeconds?: number;
|
|
17
17
|
queued?: boolean;
|
|
@@ -3,11 +3,13 @@ export const MERGE_QUEUE_RECHECK_SECONDS = 300;
|
|
|
3
3
|
/** Backstop for a missed GitHub event; events normally wake the session long before this. */
|
|
4
4
|
export const SAFETY_NET_SECONDS = 3000;
|
|
5
5
|
/** The earliest candidate as a `NextCheck`, with `at` rounded up to a whole minute. */
|
|
6
|
-
export function earliestNextCheck(candidates, nowMs = Date.now()) {
|
|
6
|
+
export function earliestNextCheck(candidates, nowMs = Date.now(), minimumSeconds = 0) {
|
|
7
7
|
const first = [...candidates].sort((a, b) => a.seconds - b.seconds)[0];
|
|
8
8
|
if (!first)
|
|
9
9
|
return undefined;
|
|
10
|
-
|
|
10
|
+
// A quota warning's interval is a floor: no wake-up may come sooner than it permits.
|
|
11
|
+
const seconds = Math.max(0, first.seconds, minimumSeconds);
|
|
12
|
+
const atMs = Math.ceil((nowMs + seconds * 1000) / 60_000) * 60_000;
|
|
11
13
|
return {
|
|
12
14
|
at: new Date(atMs).toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
13
15
|
inSeconds: Math.max(0, Math.round((atMs - nowMs) / 1000)),
|
|
@@ -53,7 +53,7 @@ export function writeWaitProgress(opts) {
|
|
|
53
53
|
export function writeDebounceProgress(tick, elapsedMs, remainingMs) {
|
|
54
54
|
const elapsedSeconds = Math.round(elapsedMs / 1000);
|
|
55
55
|
const remainingSeconds = Math.round(remainingMs / 1000);
|
|
56
|
-
process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s]
|
|
56
|
+
process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] TRIAGE — debounce ${remainingSeconds}s remaining\n`);
|
|
57
57
|
}
|
|
58
58
|
export function writeRuleAutoResolveProgress(result) {
|
|
59
59
|
if (result.ruleAutoResolve?.summary)
|
|
@@ -25,7 +25,7 @@ export function explicitInstructions(result) {
|
|
|
25
25
|
];
|
|
26
26
|
}
|
|
27
27
|
if (result.reason === "waiting" || result.reason === "timeout") {
|
|
28
|
-
return ["1. Run this aggregate
|
|
28
|
+
return ["1. Run this aggregate command again when the caller is ready to recheck."];
|
|
29
29
|
}
|
|
30
30
|
const instructions = [
|
|
31
31
|
"1. Choose each non-WAIT, non-CANCEL row that can proceed independently and run or delegate its exact `pollCommand`.",
|
|
@@ -24,7 +24,7 @@ export function planPollSummary(result, mergeRequested) {
|
|
|
24
24
|
const layer = needsOwnSession
|
|
25
25
|
? {
|
|
26
26
|
...item,
|
|
27
|
-
action: "
|
|
27
|
+
action: "triage",
|
|
28
28
|
reasons: [
|
|
29
29
|
...item.reasons,
|
|
30
30
|
staleChildren.has(item.pr) ? "stale-ancestry" : "ready-receipt-required",
|
|
@@ -108,8 +108,8 @@ function planStack(result, mergeRequested) {
|
|
|
108
108
|
if (ownedWork) {
|
|
109
109
|
const rerun = onlyDraftReadinessVerification(work.sessions, work.idle) &&
|
|
110
110
|
work.sessions.every((item) => item.owned)
|
|
111
|
-
? "If every listed one-PR probe returns `[CANCEL]` with reason `auto-mark-ready-disabled`, report the human handoff above and stop this
|
|
112
|
-
: "After the listed one-PR sessions, rerun this same
|
|
111
|
+
? "If every listed one-PR probe returns `[CANCEL]` with reason `auto-mark-ready-disabled`, report the human handoff above and stop this `stack` command. If any probe returns `[ESCALATE]`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same `stack` command when that work completes."
|
|
112
|
+
: "After the listed one-PR sessions, rerun this same `stack` command. Stop for the human handoff only when no autonomous shepherding remains.";
|
|
113
113
|
appendStackRerunInstruction(instructions, runnableCandidates, rerun);
|
|
114
114
|
}
|
|
115
115
|
return { action: stop ? "escalate" : "shepherd", stackMergeable: false, instructions };
|
|
@@ -129,7 +129,7 @@ function planStack(result, mergeRequested) {
|
|
|
129
129
|
instructions.push(`${instructions.length + 1}. PR #${item.pr} needs a one-PR session, but Shepherd could not produce its command. ${ownedWork ? "After autonomous shepherding, ask" : "Stop and ask"} for direction.`);
|
|
130
130
|
}
|
|
131
131
|
if (ownedWork) {
|
|
132
|
-
appendStackRerunInstruction(instructions, runnableCandidates, "After the listed one-PR sessions, rerun this same
|
|
132
|
+
appendStackRerunInstruction(instructions, runnableCandidates, "After the listed one-PR sessions, rerun this same `stack` command. Stop for the human handoff only when no autonomous shepherding remains.");
|
|
133
133
|
}
|
|
134
134
|
return { action: ownedWork ? "shepherd" : "escalate", stackMergeable: false, instructions };
|
|
135
135
|
}
|
|
@@ -149,10 +149,10 @@ function planStack(result, mergeRequested) {
|
|
|
149
149
|
appendAutonomousInstructions(instructions, work.sessions);
|
|
150
150
|
if (onlyDraftReadinessVerification(work.sessions, work.idle) &&
|
|
151
151
|
work.sessions.every((item) => item.owned)) {
|
|
152
|
-
instructions.push(`${instructions.length + 1}. If every listed one-PR probe returns \`[CANCEL]\` with reason \`auto-mark-ready-disabled\`, stop shepherding this stack and leave those drafts unchanged. If any probe returns \`[ESCALATE]\`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same
|
|
152
|
+
instructions.push(`${instructions.length + 1}. If every listed one-PR probe returns \`[CANCEL]\` with reason \`auto-mark-ready-disabled\`, stop shepherding this stack and leave those drafts unchanged. If any probe returns \`[ESCALATE]\`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same \`stack\` command when that work completes.`);
|
|
153
153
|
return { action: "shepherd", stackMergeable: false, instructions };
|
|
154
154
|
}
|
|
155
|
-
const action = appendStackRerunInstruction(instructions, work.sessions, "After the selected one-PR sessions, rerun this same
|
|
155
|
+
const action = appendStackRerunInstruction(instructions, work.sessions, "After the selected one-PR sessions, rerun this same `stack` command.");
|
|
156
156
|
return { action, stackMergeable: false, instructions };
|
|
157
157
|
}
|
|
158
158
|
if (open.some((item) => item.isInMergeQueue)) {
|
|
@@ -176,7 +176,7 @@ function planStack(result, mergeRequested) {
|
|
|
176
176
|
const instructions = [];
|
|
177
177
|
const sessions = open.filter((item) => item.action !== "cancel");
|
|
178
178
|
appendAutonomousInstructions(instructions, sessions);
|
|
179
|
-
const action = appendStackRerunInstruction(instructions, sessions, "After the selected one-PR sessions, rerun this same
|
|
179
|
+
const action = appendStackRerunInstruction(instructions, sessions, "After the selected one-PR sessions, rerun this same `stack` command.");
|
|
180
180
|
return { action, stackMergeable, instructions };
|
|
181
181
|
}
|
|
182
182
|
return retargetWaitPlan(open[0]);
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import type { PollSummaryResult } from "../types.mts";
|
|
2
|
+
/**
|
|
3
|
+
* Decision-relevant state only. Raw review totals and the ignored, skipped, filtered, and
|
|
4
|
+
* superseded check counts move when rules suppress a bot notice or a bot reposts a status
|
|
5
|
+
* check, which would reprint quiet polls and keep an idle stack from reaching `stall-timeout`.
|
|
6
|
+
* Post-rule `actionable` and the check transitions that drive routing stay in.
|
|
7
|
+
*/
|
|
2
8
|
export declare function summaryStatusSignature(result: PollSummaryResult): string;
|