pr-shepherd 0.56.6 → 0.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +150 -280
- package/bin/api.d.mts +25 -3
- package/bin/api.mjs +51 -13
- package/bin/checks/classify.d.mts +1 -0
- package/bin/checks/classify.mjs +1 -1
- package/bin/checks/conclusions.d.mts +5 -1
- package/bin/checks/conclusions.mjs +8 -2
- package/bin/checks/superseded.d.mts +8 -2
- package/bin/checks/superseded.mjs +24 -7
- package/bin/classify/loader.d.mts +2 -0
- package/bin/classify/loader.mjs +15 -0
- package/bin/classify/standard/author.d.mts +3 -0
- package/bin/classify/standard/author.mjs +10 -0
- package/bin/classify/standard/codecov-report.d.mts +4 -0
- package/bin/classify/standard/codecov-report.mjs +10 -0
- package/bin/classify/standard/coderabbit-notices.d.mts +8 -0
- package/bin/classify/standard/coderabbit-notices.mjs +38 -0
- package/bin/classify/standard/codex-notices.d.mts +8 -0
- package/bin/classify/standard/codex-notices.mjs +34 -0
- package/bin/classify/standard/dependabot-notices.d.mts +4 -0
- package/bin/classify/standard/dependabot-notices.mjs +15 -0
- package/bin/classify/standard/gemini-notices.d.mts +4 -0
- package/bin/classify/standard/gemini-notices.mjs +13 -0
- package/bin/classify/standard/index.d.mts +7 -0
- package/bin/classify/standard/index.mjs +21 -0
- package/bin/classify/standard/kilo-no-issues.d.mts +4 -0
- package/bin/classify/standard/kilo-no-issues.mjs +17 -0
- package/bin/classify/standard/sourcery-notices.d.mts +8 -0
- package/bin/classify/standard/sourcery-notices.mjs +20 -0
- package/bin/cli/api-usage-formatter.mjs +6 -1
- package/bin/cli/apply-merge-handler.d.mts +3 -0
- package/bin/cli/apply-merge-handler.mjs +73 -0
- package/bin/cli/arg-flags.d.mts +2 -0
- package/bin/cli/arg-flags.mjs +47 -0
- package/bin/cli/args.mjs +10 -41
- package/bin/cli/clean-handler.d.mts +1 -0
- package/bin/cli/clean-handler.mjs +71 -0
- package/bin/cli/default-poll.mjs +3 -0
- package/bin/cli/event-state-scope.d.mts +19 -0
- package/bin/cli/event-state-scope.mjs +60 -0
- package/bin/cli/fix-formatter.mjs +5 -11
- package/bin/cli/handlers.d.mts +0 -1
- package/bin/cli/handlers.mjs +8 -64
- package/bin/cli/help-command-pages.d.mts +62 -18
- package/bin/cli/help-command-pages.mjs +59 -15
- package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.mjs +6 -0
- package/bin/cli/help-log-file-page.d.mts +1 -1
- package/bin/cli/help-log-file-page.mjs +4 -1
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +5 -0
- package/bin/cli/help.d.mts +63 -19
- package/bin/cli/help.mjs +2 -0
- package/bin/cli/iterate-branch-segment.d.mts +2 -0
- package/bin/cli/iterate-branch-segment.mjs +12 -7
- package/bin/cli/iterate-flags.d.mts +7 -1
- package/bin/cli/iterate-flags.mjs +38 -0
- package/bin/cli/iterate-formatter.d.mts +6 -6
- package/bin/cli/iterate-formatter.mjs +56 -22
- package/bin/cli/iterate-instructions.mjs +45 -15
- package/bin/cli/iterate-lean.mjs +39 -14
- package/bin/cli/iterate-merge-formatter.d.mts +6 -0
- package/bin/cli/iterate-merge-formatter.mjs +24 -14
- package/bin/cli/iterate-verbose.mjs +3 -6
- package/bin/cli/list-formatters.mjs +3 -1
- package/bin/cli/mutate-formatter.mjs +10 -5
- package/bin/cli/next-check-format.d.mts +7 -0
- package/bin/cli/next-check-format.mjs +10 -0
- package/bin/cli/playbook-handler.d.mts +5 -0
- package/bin/cli/playbook-handler.mjs +40 -0
- package/bin/cli/poll-handler.mjs +7 -6
- package/bin/cli/poll-summary-formatter.mjs +11 -2
- package/bin/cli/poll-targets.mjs +2 -0
- package/bin/cli/resolve-validators.mjs +2 -2
- package/bin/cli/runner.mjs +6 -1
- package/bin/cli/stack-layer-view.d.mts +33 -0
- package/bin/cli/stack-layer-view.mjs +1 -0
- package/bin/cli/stack-overview.d.mts +10 -28
- package/bin/cli/stack-overview.mjs +39 -14
- package/bin/cli/transport-args.d.mts +5 -0
- package/bin/cli/transport-args.mjs +23 -0
- package/bin/cli/transport-formatter.d.mts +9 -0
- package/bin/cli/transport-formatter.mjs +13 -0
- package/bin/cli-parser.mjs +62 -7
- package/bin/commands/apply-merge-options.d.mts +4 -0
- package/bin/commands/apply-merge-options.mjs +34 -0
- package/bin/commands/apply-merge-output.d.mts +7 -0
- package/bin/commands/apply-merge-output.mjs +20 -0
- package/bin/commands/apply-merge-pull.d.mts +20 -0
- package/bin/commands/apply-merge-pull.mjs +15 -0
- package/bin/commands/apply-merge.d.mts +18 -0
- package/bin/commands/apply-merge.mjs +126 -0
- package/bin/commands/apply-queue-removal.mjs +7 -0
- package/bin/commands/check-annotations.mjs +28 -18
- package/bin/commands/check-execution-context.d.mts +5 -0
- package/bin/commands/check-execution-context.mjs +16 -1
- package/bin/commands/check-fingerprint.d.mts +26 -2
- package/bin/commands/check-fingerprint.mjs +64 -28
- package/bin/commands/check-terminal-report.mjs +11 -1
- package/bin/commands/check-unreported.d.mts +6 -2
- package/bin/commands/check-unreported.mjs +32 -12
- package/bin/commands/check-wait-detectors.d.mts +35 -0
- package/bin/commands/check-wait-detectors.mjs +107 -0
- package/bin/commands/check.d.mts +4 -2
- package/bin/commands/check.mjs +130 -40
- package/bin/commands/event-instructions.d.mts +11 -0
- package/bin/commands/event-instructions.mjs +27 -0
- package/bin/commands/event-mode.d.mts +14 -0
- package/bin/commands/event-mode.mjs +138 -0
- package/bin/commands/iterate/api-usage.mjs +4 -2
- package/bin/commands/iterate/base.mjs +4 -0
- package/bin/commands/iterate/check-blocker-gate.mjs +14 -2
- package/bin/commands/iterate/check-instructions.d.mts +9 -10
- package/bin/commands/iterate/check-instructions.mjs +23 -22
- package/bin/commands/iterate/classify.mjs +19 -5
- package/bin/commands/iterate/escalate.mjs +13 -3
- package/bin/commands/iterate/fix-code.mjs +26 -9
- package/bin/commands/iterate/fix-instruction-threads.d.mts +0 -8
- package/bin/commands/iterate/fix-instruction-threads.mjs +0 -16
- package/bin/commands/iterate/fix-loop-instruction.d.mts +9 -0
- package/bin/commands/iterate/fix-loop-instruction.mjs +35 -0
- package/bin/commands/iterate/helpers.mjs +15 -2
- package/bin/commands/iterate/index.mjs +57 -16
- package/bin/commands/iterate/mark-ready.mjs +50 -10
- package/bin/commands/iterate/merge-plan.d.mts +19 -0
- package/bin/commands/iterate/merge-plan.mjs +88 -0
- package/bin/commands/iterate/merge-state.mjs +3 -0
- package/bin/commands/iterate/merge.d.mts +4 -15
- package/bin/commands/iterate/merge.mjs +18 -51
- package/bin/commands/iterate/merged-base-pull-requests.d.mts +2 -6
- package/bin/commands/iterate/merged-base-pull-requests.mjs +15 -11
- package/bin/commands/iterate/native-stack-rebase.mjs +11 -0
- package/bin/commands/iterate/render.d.mts +8 -2
- package/bin/commands/iterate/render.mjs +28 -42
- package/bin/commands/iterate/stack-trunk-conflict.mjs +17 -1
- package/bin/commands/iterate/stale-ancestry.mjs +4 -1
- package/bin/commands/iterate/stall.d.mts +5 -1
- package/bin/commands/iterate/stall.mjs +19 -7
- package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
- package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
- package/bin/commands/iterate/unreported-required.mjs +5 -2
- package/bin/commands/mark-files-as-viewed.mjs +7 -0
- package/bin/commands/next-check.d.mts +31 -0
- package/bin/commands/next-check.mjs +46 -0
- package/bin/commands/playbook.d.mts +13 -0
- package/bin/commands/playbook.mjs +48 -0
- package/bin/commands/poll-mode.d.mts +8 -0
- package/bin/commands/poll-mode.mjs +14 -0
- package/bin/commands/poll-progress.d.mts +1 -0
- package/bin/commands/poll-progress.mjs +6 -3
- package/bin/commands/poll-quota.d.mts +3 -2
- package/bin/commands/poll-quota.mjs +7 -3
- package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
- package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
- package/bin/commands/poll-summary-instructions.mjs +16 -17
- package/bin/commands/poll-summary.mjs +42 -17
- package/bin/commands/poll.d.mts +0 -1
- package/bin/commands/poll.mjs +11 -19
- package/bin/commands/quota-selection.d.mts +2 -1
- package/bin/commands/quota-selection.mjs +12 -7
- package/bin/commands/resolve-mutate.mjs +33 -5
- package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
- package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
- package/bin/commands/rule-auto-resolve.mjs +9 -3
- package/bin/commands/shepherd-journal.d.mts +5 -4
- package/bin/commands/shepherd-journal.mjs +9 -8
- package/bin/commands/stack-drain.d.mts +1 -9
- package/bin/commands/stack-drain.mjs +50 -37
- package/bin/commands/stack-handoffs.d.mts +10 -0
- package/bin/commands/stack-handoffs.mjs +25 -0
- package/bin/commands/stack-stall.d.mts +5 -0
- package/bin/commands/stack-stall.mjs +11 -0
- package/bin/commands/stack-work.d.mts +5 -0
- package/bin/commands/stack-work.mjs +32 -0
- package/bin/comments/existing-reply-scan.d.mts +17 -0
- package/bin/comments/existing-reply-scan.mjs +41 -0
- package/bin/comments/rate-limit.mjs +2 -2
- package/bin/comments/resolve.d.mts +7 -1
- package/bin/comments/resolve.mjs +108 -18
- package/bin/comments/rest-reply.d.mts +9 -0
- package/bin/comments/rest-reply.mjs +95 -0
- package/bin/comments/rest-review-mutations.d.mts +22 -0
- package/bin/comments/rest-review-mutations.mjs +125 -0
- package/bin/comments/review-visibility.d.mts +1 -1
- package/bin/comments/review-visibility.mjs +3 -2
- package/bin/comments/sha-poll.mjs +7 -3
- package/bin/comments/thread-visibility.mjs +10 -5
- package/bin/comments/uncertain-replies.d.mts +11 -0
- package/bin/comments/uncertain-replies.mjs +121 -0
- package/bin/comments/uncertain-reply-guards.d.mts +10 -0
- package/bin/comments/uncertain-reply-guards.mjs +94 -0
- package/bin/comments/visible-comments.d.mts +1 -1
- package/bin/comments/visible-comments.mjs +4 -2
- package/bin/config/load.d.mts +37 -1
- package/bin/config/load.mjs +73 -1
- package/bin/config.json +6 -2
- package/bin/exit-codes.d.mts +3 -0
- package/bin/exit-codes.mjs +8 -0
- package/bin/github/api-telemetry-aggregate.d.mts +15 -4
- package/bin/github/api-telemetry-aggregate.mjs +57 -18
- package/bin/github/api-telemetry.d.mts +4 -0
- package/bin/github/api-telemetry.mjs +22 -8
- package/bin/github/apply-review-preflight.d.mts +20 -0
- package/bin/github/apply-review-preflight.mjs +61 -0
- package/bin/github/base-behind-cache.d.mts +15 -0
- package/bin/github/base-behind-cache.mjs +18 -0
- package/bin/github/batch-parse-suites.d.mts +4 -2
- package/bin/github/batch-parse-suites.mjs +6 -1
- package/bin/github/batch-parser-helpers.mjs +40 -2
- package/bin/github/batch-parsers.mjs +1 -0
- package/bin/github/batch-raw-rules.d.mts +26 -0
- package/bin/github/batch-raw-types.d.mts +8 -6
- package/bin/github/batch-stack-evidence.d.mts +28 -0
- package/bin/github/batch-stack-evidence.mjs +62 -0
- package/bin/github/batch.d.mts +15 -2
- package/bin/github/batch.mjs +34 -17
- package/bin/github/captured-proxy-agent.d.mts +3 -0
- package/bin/github/captured-proxy-agent.mjs +8 -0
- package/bin/github/check-annotations-batch.d.mts +0 -5
- package/bin/github/check-annotations-batch.mjs +25 -4
- package/bin/github/check-annotations.mjs +3 -1
- package/bin/github/client.d.mts +8 -19
- package/bin/github/client.mjs +45 -13
- package/bin/github/fingerprint-fields.d.mts +8 -0
- package/bin/github/fingerprint-fields.mjs +6 -1
- package/bin/github/fingerprint.d.mts +4 -2
- package/bin/github/fingerprint.mjs +6 -38
- package/bin/github/github-fetch.d.mts +2 -0
- package/bin/github/github-fetch.mjs +32 -0
- package/bin/github/gql/base-behind.gql +3 -0
- package/bin/github/gql/batch-pr-page.gql +16 -0
- package/bin/github/gql/batch-pr.gql +57 -0
- package/bin/github/gql/commit-check-contexts.gql +17 -0
- package/bin/github/gql/get-pr-body.gql +1 -0
- package/bin/github/gql/poll-stack-summary.gql +1 -0
- package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
- package/bin/github/gql/poll-summary-fragment.gql +65 -15
- package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
- package/bin/github/graphql-http.mjs +9 -4
- package/bin/github/graphql-internal-retry.d.mts +2 -0
- package/bin/github/graphql-internal-retry.mjs +1 -1
- package/bin/github/graphql-quota-probe.d.mts +6 -0
- package/bin/github/graphql-quota-probe.mjs +56 -0
- package/bin/github/graphql-session-refusal.d.mts +8 -0
- package/bin/github/graphql-session-refusal.mjs +14 -0
- package/bin/github/http-request.d.mts +1 -1
- package/bin/github/http-request.mjs +22 -6
- package/bin/github/http-utils.mjs +6 -0
- package/bin/github/legacy-dispatcher.d.mts +6 -0
- package/bin/github/legacy-dispatcher.mjs +44 -0
- package/bin/github/merge-target-rules.d.mts +12 -3
- package/bin/github/merge-target-rules.mjs +71 -34
- package/bin/github/mutation-policy.d.mts +6 -0
- package/bin/github/mutation-policy.mjs +21 -0
- package/bin/github/poll-summary-annotation-probe.mjs +17 -10
- package/bin/github/poll-summary-check-blockers.d.mts +1 -1
- package/bin/github/poll-summary-check-blockers.mjs +2 -2
- package/bin/github/poll-summary-check-hydration.mjs +25 -10
- package/bin/github/poll-summary-checks.d.mts +2 -2
- package/bin/github/poll-summary-checks.mjs +9 -6
- package/bin/github/poll-summary-command.d.mts +2 -0
- package/bin/github/poll-summary-command.mjs +24 -0
- package/bin/github/poll-summary-fingerprint.d.mts +1 -1
- package/bin/github/poll-summary-fingerprint.mjs +4 -2
- package/bin/github/poll-summary-handoff-fields.d.mts +3 -0
- package/bin/github/poll-summary-handoff-fields.mjs +73 -0
- package/bin/github/poll-summary-projector.d.mts +1 -1
- package/bin/github/poll-summary-projector.mjs +17 -24
- package/bin/github/poll-summary-raw.d.mts +70 -10
- package/bin/github/poll-summary-readiness.d.mts +6 -1
- package/bin/github/poll-summary-readiness.mjs +13 -4
- package/bin/github/poll-summary-rest-queue.d.mts +3 -0
- package/bin/github/poll-summary-rest-queue.mjs +12 -0
- package/bin/github/poll-summary-review.d.mts +1 -1
- package/bin/github/poll-summary-review.mjs +35 -20
- package/bin/github/poll-summary-route.mjs +19 -3
- package/bin/github/poll-summary-stack-page.d.mts +11 -0
- package/bin/github/poll-summary-stack-page.mjs +27 -0
- package/bin/github/poll-summary-unreported.d.mts +3 -1
- package/bin/github/poll-summary-unreported.mjs +6 -3
- package/bin/github/poll-summary.d.mts +4 -1
- package/bin/github/poll-summary.mjs +47 -33
- package/bin/github/queries.d.mts +7 -3
- package/bin/github/queries.mjs +8 -4
- package/bin/github/rate-limit-sample.d.mts +7 -0
- package/bin/github/rate-limit-sample.mjs +12 -0
- package/bin/github/reply-recovery-read.d.mts +50 -0
- package/bin/github/reply-recovery-read.mjs +118 -0
- package/bin/github/reply-thread-transcripts.d.mts +0 -1
- package/bin/github/reply-thread-transcripts.mjs +24 -1
- package/bin/github/rest-annotation-read.d.mts +5 -0
- package/bin/github/rest-annotation-read.mjs +44 -0
- package/bin/github/rest-batch-read.d.mts +12 -0
- package/bin/github/rest-batch-read.mjs +57 -0
- package/bin/github/rest-batch-snapshot.d.mts +30 -0
- package/bin/github/rest-batch-snapshot.mjs +115 -0
- package/bin/github/rest-batch-unavailable.d.mts +5 -0
- package/bin/github/rest-batch-unavailable.mjs +16 -0
- package/bin/github/rest-check-read.d.mts +34 -0
- package/bin/github/rest-check-read.mjs +124 -0
- package/bin/github/rest-comment-authorship.d.mts +7 -0
- package/bin/github/rest-comment-authorship.mjs +11 -0
- package/bin/github/rest-conditional-scope.d.mts +20 -0
- package/bin/github/rest-conditional-scope.mjs +44 -0
- package/bin/github/rest-explicit-summary-read.d.mts +3 -0
- package/bin/github/rest-explicit-summary-read.mjs +8 -0
- package/bin/github/rest-feedback-read.d.mts +36 -0
- package/bin/github/rest-feedback-read.mjs +147 -0
- package/bin/github/rest-feedback-validation.d.mts +5 -0
- package/bin/github/rest-feedback-validation.mjs +28 -0
- package/bin/github/rest-http.d.mts +12 -0
- package/bin/github/rest-http.mjs +108 -23
- package/bin/github/rest-identities.d.mts +22 -0
- package/bin/github/rest-identities.mjs +134 -0
- package/bin/github/rest-merge-stack-guard.d.mts +14 -0
- package/bin/github/rest-merge-stack-guard.mjs +41 -0
- package/bin/github/rest-merge.d.mts +29 -0
- package/bin/github/rest-merge.mjs +56 -0
- package/bin/github/rest-merged-base-read.d.mts +9 -0
- package/bin/github/rest-merged-base-read.mjs +48 -0
- package/bin/github/rest-pr-core.d.mts +52 -0
- package/bin/github/rest-pr-core.mjs +60 -0
- package/bin/github/rest-reader-core.d.mts +16 -0
- package/bin/github/rest-reader-core.mjs +154 -0
- package/bin/github/rest-review-read.d.mts +11 -0
- package/bin/github/rest-review-read.mjs +47 -0
- package/bin/github/rest-rules-read.d.mts +9 -0
- package/bin/github/rest-rules-read.mjs +154 -0
- package/bin/github/rest-session-refusal.d.mts +5 -0
- package/bin/github/rest-session-refusal.mjs +21 -0
- package/bin/github/rest-snapshot-context.d.mts +10 -0
- package/bin/github/rest-snapshot-context.mjs +1 -0
- package/bin/github/rest-stack-read.d.mts +21 -0
- package/bin/github/rest-stack-read.mjs +47 -0
- package/bin/github/rest-stack-summary.d.mts +8 -0
- package/bin/github/rest-stack-summary.mjs +62 -0
- package/bin/github/rest-summary-read.d.mts +7 -0
- package/bin/github/rest-summary-read.mjs +112 -0
- package/bin/github/rest-terminal-batch.d.mts +14 -0
- package/bin/github/rest-terminal-batch.mjs +45 -0
- package/bin/github/rest-text.mjs +7 -3
- package/bin/github/rest-thread-read.d.mts +4 -0
- package/bin/github/rest-thread-read.mjs +35 -0
- package/bin/github/rest-viewer-read.d.mts +2 -0
- package/bin/github/rest-viewer-read.mjs +41 -0
- package/bin/github/stack-bottom.d.mts +3 -0
- package/bin/github/stack-bottom.mjs +14 -0
- package/bin/github/stack-handoff-context.d.mts +25 -0
- package/bin/github/stack-handoff-context.mjs +12 -0
- package/bin/github/stack-handoff-page.d.mts +17 -0
- package/bin/github/stack-handoff-page.mjs +306 -0
- package/bin/github/stack-handoff.d.mts +16 -0
- package/bin/github/stack-handoff.mjs +115 -0
- package/bin/github/stack-order.d.mts +6 -0
- package/bin/github/stack-order.mjs +13 -0
- package/bin/github/stack-read.d.mts +7 -5
- package/bin/github/stack-read.mjs +22 -24
- package/bin/github/stack-size-hint.d.mts +3 -0
- package/bin/github/stack-size-hint.mjs +14 -0
- package/bin/github/suggestion-thread.mjs +27 -1
- package/bin/github/thread-comments.mjs +9 -1
- package/bin/github/transport-evidence.d.mts +20 -0
- package/bin/github/transport-evidence.mjs +26 -0
- package/bin/github/transport-mode.d.mts +2 -0
- package/bin/github/transport-mode.mjs +5 -0
- package/bin/github/transport.d.mts +19 -0
- package/bin/github/transport.mjs +126 -0
- package/bin/github/unsupported-rest.d.mts +4 -0
- package/bin/github/unsupported-rest.mjs +7 -0
- package/bin/mcp/server.mjs +52 -3
- package/bin/merge-status/derive.d.mts +2 -0
- package/bin/merge-status/derive.mjs +17 -13
- package/bin/merge-status/required-check-requirements.d.mts +7 -0
- package/bin/merge-status/required-check-requirements.mjs +15 -0
- package/bin/merge-status/requirements-format.d.mts +5 -0
- package/bin/merge-status/requirements-format.mjs +41 -12
- package/bin/merge-status/requirements.mjs +18 -7
- package/bin/state/base.mjs +41 -0
- package/bin/state/durable-state.d.mts +6 -0
- package/bin/state/durable-state.mjs +41 -0
- package/bin/state/graphql-quota-policy.mjs +23 -16
- package/bin/state/merge-request.d.mts +25 -0
- package/bin/state/merge-request.mjs +145 -0
- package/bin/state/pr-fingerprint.d.mts +15 -0
- package/bin/state/pr-fingerprint.mjs +22 -7
- package/bin/state/rest-cache.d.mts +3 -0
- package/bin/state/rest-cache.mjs +1 -0
- package/bin/state/rest-snapshot-report.d.mts +21 -0
- package/bin/state/rest-snapshot-report.mjs +52 -0
- package/bin/state/seen-comments.d.mts +3 -0
- package/bin/state/seen-comments.mjs +18 -0
- package/bin/types/api-usage.d.mts +9 -1
- package/bin/types/escalate.d.mts +1 -1
- package/bin/types/github.d.mts +23 -9
- package/bin/types/iterate.d.mts +24 -2
- package/bin/types/merge-action.d.mts +1 -1
- package/bin/types/merge-requirements.d.mts +7 -7
- package/bin/types/next-check.d.mts +12 -0
- package/bin/types/next-check.mjs +1 -0
- package/bin/types/poll-summary.d.mts +21 -0
- package/bin/types/report.d.mts +13 -1
- package/bin/types/review-thread.d.mts +1 -1
- package/bin/types.d.mts +1 -0
- package/bin/types.mjs +1 -0
- package/package.json +4 -3
- 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/mark-files-as-viewed/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
- package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
- package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +3 -1
- package/bin/github/gql/pr-fingerprint.gql +0 -75
- package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
- package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
- package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// The `[FIX_CODE]` heading and the skill already say the action is non-terminal.
|
|
3
|
+
const FIX_CODE_CONTINUATION = "Rerun Shepherd now.";
|
|
4
|
+
/** The `[FIX_CODE]` recurrence step, whether plain or rewritten by the quota warning. */
|
|
5
|
+
export function isFixCodeContinuation(step) {
|
|
6
|
+
return step === FIX_CODE_CONTINUATION || /\[FIX_CODE\].*non-terminal/i.test(step);
|
|
7
|
+
}
|
|
8
|
+
/** Build the stale-CR step for `## Changes-requested reviews`. Empty when no human CR is stale. */
|
|
4
9
|
export function buildCrStaleClause(reviews) {
|
|
5
|
-
|
|
6
|
-
? "
|
|
10
|
+
return reviews.some((r) => r.staleReview && !r.staleBotCr)
|
|
11
|
+
? "`[stale]` changes-requested reviews are human CRs on an old commit. Ask the reviewer to re-review."
|
|
7
12
|
: "";
|
|
8
|
-
return human;
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* Build the optional branch-update hint. Empty unless the branch is behind or conflicts with its
|
|
@@ -42,27 +46,21 @@ export function buildRepeatedWorkflowBranchRecoveryInstructions(baseBranch, hasE
|
|
|
42
46
|
return instructions;
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
|
-
* Build the `
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* Contrast with what *does* stay in the skill's "Review-mutation mechanics" playbook —
|
|
51
|
-
* dismiss-ID retention. The pointer below is load-bearing: without it, nothing in CLI output
|
|
52
|
-
* tells the agent that playbook exists.
|
|
49
|
+
* Build the `apply review` step, with the rendered command inline. The step stays here (not in
|
|
50
|
+
* the skill) because the *as-printed* command is invalid without `$DISMISS_MESSAGE`: an empty
|
|
51
|
+
* `--message` makes `apply review` reject the mutation. `--require-sha` needs no substitution:
|
|
52
|
+
* the rendered command reads the pushed HEAD itself. "Even if you changed no code": the
|
|
53
|
+
* command records each item's disposition either way.
|
|
53
54
|
*/
|
|
54
|
-
export function buildResolveCommandInstruction(resolveCommand) {
|
|
55
|
+
export function buildResolveCommandInstruction(resolveCommand, rendered) {
|
|
55
56
|
if (!resolveCommand.hasMutations)
|
|
56
57
|
return [];
|
|
57
|
-
const instructions = [];
|
|
58
|
-
if (resolveCommand.requiresHeadSha) {
|
|
59
|
-
instructions.push("If you did not change code, replace `$HEAD_SHA` with `$(git rev-parse HEAD)` (it must equal the remote PR head). If you did, use the pushed SHA.");
|
|
60
|
-
}
|
|
61
58
|
if (resolveCommand.requiresDismissMessage) {
|
|
62
|
-
|
|
59
|
+
return [
|
|
60
|
+
`\`$DISMISS_MESSAGE\`: one sentence on what changed. Run even if no code changed: \`${rendered}\``,
|
|
61
|
+
];
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
return instructions;
|
|
63
|
+
return [`Run, even if no code changed: \`${rendered}\``];
|
|
66
64
|
}
|
|
67
65
|
/** One pointer. Conclusion, rerun, and bare-check rules live in the CI playbook. */
|
|
68
66
|
export function buildFailingCheckInstructions(checks) {
|
|
@@ -72,9 +70,12 @@ export function buildFailingCheckInstructions(checks) {
|
|
|
72
70
|
}
|
|
73
71
|
/** Update the PR branch after an external blocker merges or closes. Never rerun that job. */
|
|
74
72
|
export function buildReleasedBlockerInstruction(prNumber) {
|
|
75
|
-
return
|
|
73
|
+
return getGithubTransport() === "rest"
|
|
74
|
+
? "Update this PR branch from its base using the repository's branch-update procedure. Do not rerun the job; a rerun retests the old merge ref."
|
|
75
|
+
: `Update this PR branch from its base with \`gh pr update-branch ${prNumber} --rebase\`. Do not rerun the job; a rerun retests the old merge ref.`;
|
|
76
76
|
}
|
|
77
77
|
/** Recurrence only. Commit, push, rerun, and SHA steps are earlier instructions. */
|
|
78
78
|
export function buildFixCompletionInstruction() {
|
|
79
79
|
return FIX_CODE_CONTINUATION;
|
|
80
80
|
}
|
|
81
|
+
import { getGithubTransport } from "../../github/transport.mjs";
|
|
@@ -2,6 +2,8 @@ import { buildPrShepherdCommand } from "../../cli/runner.mjs";
|
|
|
2
2
|
import { shouldMinimizeAuthor } from "../../comments/minimize-policy.mjs";
|
|
3
3
|
import { isConfiguredBotAuthor, isHumanAuthor, } from "../../comments/authors.mjs";
|
|
4
4
|
import { buildThreadMutationRouting } from "./thread-mutation-routing.mjs";
|
|
5
|
+
import { canGenerateGithubMutation } from "../../github/mutation-policy.mjs";
|
|
6
|
+
import { durableStateRequested } from "../../state/durable-state.mjs";
|
|
5
7
|
function dedupeIds(ids) {
|
|
6
8
|
const seen = new Set();
|
|
7
9
|
const out = [];
|
|
@@ -15,7 +17,7 @@ function dedupeIds(ids) {
|
|
|
15
17
|
}
|
|
16
18
|
export function classifyReviewSummaries(summaries, approvals, minimizeApprovals, minimizeComments = "all", botUsernames = new Set(), unresolvedThreads = [], ruleAutoResolveIds = []) {
|
|
17
19
|
const blockedReviewIds = new Set(unresolvedThreads.flatMap((t) => (t.reviewId !== undefined ? [t.reviewId] : [])));
|
|
18
|
-
const eligible = (r) => r.viewerCanMinimize
|
|
20
|
+
const eligible = (r) => canGenerateGithubMutation(r.viewerCanMinimize, "minimize") &&
|
|
19
21
|
shouldMinimizeAuthor(r.authorType, minimizeComments, r.author, botUsernames) &&
|
|
20
22
|
!blockedReviewIds.has(r.id);
|
|
21
23
|
// First-look summaries still need one tick to surface their body to the agent,
|
|
@@ -36,7 +38,7 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
36
38
|
if (minimizeApprovals) {
|
|
37
39
|
const surfacedApprovals = [];
|
|
38
40
|
for (const r of approvals) {
|
|
39
|
-
if (r.viewerCanMinimize
|
|
41
|
+
if (canGenerateGithubMutation(r.viewerCanMinimize, "minimize") &&
|
|
40
42
|
shouldMinimizeAuthor(r.authorType, minimizeComments, r.author, botUsernames))
|
|
41
43
|
minimizeIds.push(r.id);
|
|
42
44
|
else
|
|
@@ -58,14 +60,24 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
58
60
|
surfacedApprovals: approvals,
|
|
59
61
|
};
|
|
60
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* A durable-state session (event mode or a cloud session) can lose its uncertain-reply records
|
|
65
|
+
* between ticks. Its generated reply command opts in to adopting a reply GitHub already shows,
|
|
66
|
+
* so rerunning the same printed command cannot double-post. Direct apply requests never do.
|
|
67
|
+
*/
|
|
68
|
+
function pushAdoptExistingReplies(argv, hasReply) {
|
|
69
|
+
if (hasReply && durableStateRequested())
|
|
70
|
+
argv.push("--adopt-existing-replies");
|
|
71
|
+
}
|
|
61
72
|
export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentIds, reviews, checks, prReference, botUsernames = new Set(), ruleAutoResolveThreadIds = [], viewerAuthorization, authorizationThreads = [], resolveOtherHumanThreads = "none") {
|
|
62
73
|
const allThreads = [...threads, ...resolutionOnlyThreads];
|
|
63
74
|
const routed = buildThreadMutationRouting(allThreads, botUsernames, ruleAutoResolveThreadIds, resolveOtherHumanThreads);
|
|
64
75
|
const canReply = new Set(authorizationThreads
|
|
65
|
-
.filter((thread) => thread.viewerCanReply
|
|
76
|
+
.filter((thread) => canGenerateGithubMutation(thread.viewerCanReply, "reply"))
|
|
66
77
|
.map((thread) => thread.id));
|
|
67
78
|
const canResolve = new Set(authorizationThreads
|
|
68
|
-
.filter((thread) => thread.
|
|
79
|
+
.filter((thread) => thread.isResolved === false &&
|
|
80
|
+
canGenerateGithubMutation(thread.viewerCanResolve, "resolve"))
|
|
69
81
|
.map((thread) => thread.id));
|
|
70
82
|
const pairedResolveIds = new Set(routed.pairedResolveThreadIds);
|
|
71
83
|
const replyThreadIds = routed.replyThreadIds.filter((id) => canReply.has(id) && (!pairedResolveIds.has(id) || canResolve.has(id)));
|
|
@@ -76,7 +88,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
76
88
|
const resolveThreadIds = dedupeIds([...pairedResolveThreadIds, ...standaloneResolveThreadIds]);
|
|
77
89
|
// Bot/non-human CHANGES_REQUESTED reviews are auto-dismissed after the agent pushes a fix.
|
|
78
90
|
// Human reviews are left for the reviewer to re-review or dismiss themselves.
|
|
79
|
-
const dismissReviewIds = dedupeIds(viewerAuthorization?.viewerCanAdminister
|
|
91
|
+
const dismissReviewIds = dedupeIds(canGenerateGithubMutation(viewerAuthorization?.viewerCanAdminister, "dismiss")
|
|
80
92
|
? reviews
|
|
81
93
|
.filter((r) => !isHumanAuthor(r) || isConfiguredBotAuthor(r, botUsernames))
|
|
82
94
|
.map((r) => r.id)
|
|
@@ -99,6 +111,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
99
111
|
resolveArgv.push("--resolve-thread-ids", pairedResolveThreadIds.join(","));
|
|
100
112
|
}
|
|
101
113
|
resolveArgv.push("--message", "$DISMISS_MESSAGE");
|
|
114
|
+
pushAdoptExistingReplies(resolveArgv, hasReply);
|
|
102
115
|
if (hasDismiss) {
|
|
103
116
|
resolveArgv.push("--dismiss-review-ids", dismissReviewIds.join(","));
|
|
104
117
|
}
|
|
@@ -139,6 +152,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
139
152
|
if (replyThreadIds.length > 0) {
|
|
140
153
|
argv.push("--reply-thread-ids", replyThreadIds.join(","));
|
|
141
154
|
argv.push("--message", "$DISMISS_MESSAGE");
|
|
155
|
+
pushAdoptExistingReplies(argv, true);
|
|
142
156
|
}
|
|
143
157
|
if (resolveThreadIds.length > 0) {
|
|
144
158
|
argv.push("--resolve-thread-ids", resolveThreadIds.join(","));
|
|
@@ -151,9 +151,19 @@ export function buildEscalateHumanMessage(escalate, pr, opts) {
|
|
|
151
151
|
const loc = t.path ? `\`${t.path}:${t.line ?? "?"}\`` : "(no location)";
|
|
152
152
|
lines.push(`- thread \`${t.id}\` — ${loc} (${renderEscalateAuthor(t)}):`);
|
|
153
153
|
lines.push("");
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
if (!t.comments?.length) {
|
|
155
|
+
for (const bodyLine of t.body.split("\n"))
|
|
156
|
+
lines.push(` > ${bodyLine}`);
|
|
157
|
+
lines.push("");
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
// The transcript starts with the root comment, whose body is the thread body.
|
|
161
|
+
for (const c of t.comments) {
|
|
162
|
+
lines.push(` - comment \`${c.id}\` (${renderEscalateAuthor(c)}):`, "");
|
|
163
|
+
for (const bodyLine of c.body.split("\n"))
|
|
164
|
+
lines.push(` > ${bodyLine}`);
|
|
165
|
+
lines.push("");
|
|
166
|
+
}
|
|
157
167
|
}
|
|
158
168
|
for (const r of escalate.changesRequestedReviews) {
|
|
159
169
|
lines.push(`- review \`${r.id}\` (${renderEscalateAuthor(r)}):`);
|
|
@@ -20,6 +20,7 @@ import { annotationMarkerBody, checksWithActionableAnnotations } from "../check-
|
|
|
20
20
|
import { threadTranscriptBody } from "../../threads/transcript.mjs";
|
|
21
21
|
import { isHumanAuthor, isConfiguredBotAuthor } from "../../comments/authors.mjs";
|
|
22
22
|
import { canRerunWorkflows } from "../../checks/conclusions.mjs";
|
|
23
|
+
import { canGenerateGithubMutation } from "../../github/mutation-policy.mjs";
|
|
23
24
|
import { loadConfig } from "../../config/load.mjs";
|
|
24
25
|
import { formatPrUrl } from "../../pr-reference.mjs";
|
|
25
26
|
const EMPTY_RELEASED = new Set();
|
|
@@ -87,10 +88,12 @@ export async function handleFixCode(ctx) {
|
|
|
87
88
|
const routedThreadMutations = buildThreadMutationRouting(allThreads, botUsernames, [...ruleAutoResolveIds], resolveOtherHumanThreads);
|
|
88
89
|
const replyIdSet = new Set(routedThreadMutations.replyThreadIds);
|
|
89
90
|
const resolveIdSet = new Set(routedThreadMutations.resolveThreadIds);
|
|
90
|
-
const unauthorizedReplies = allThreads.filter((thread) => replyIdSet.has(thread.id) && thread.viewerCanReply
|
|
91
|
-
const unauthorizedResolves = allThreads.filter((thread) => resolveIdSet.has(thread.id) &&
|
|
91
|
+
const unauthorizedReplies = allThreads.filter((thread) => replyIdSet.has(thread.id) && !canGenerateGithubMutation(thread.viewerCanReply, "reply"));
|
|
92
|
+
const unauthorizedResolves = allThreads.filter((thread) => resolveIdSet.has(thread.id) &&
|
|
93
|
+
(thread.isResolved !== false ||
|
|
94
|
+
!canGenerateGithubMutation(thread.viewerCanResolve, "resolve")));
|
|
92
95
|
const unauthorizedDismissals = report.changesRequestedReviews.filter((review) => (!isHumanAuthor(review) || isConfiguredBotAuthor(review, botUsernames)) &&
|
|
93
|
-
report.viewerAuthorization?.viewerCanAdminister
|
|
96
|
+
!canGenerateGithubMutation(report.viewerAuthorization?.viewerCanAdminister, "dismiss"));
|
|
94
97
|
const skippedThreadIds = new Set([...unauthorizedReplies, ...unauthorizedResolves].map((thread) => thread.id));
|
|
95
98
|
const mutationActionableThreads = report.threads.actionable.filter((thread) => !skippedThreadIds.has(thread.id) &&
|
|
96
99
|
((thread.path !== null && thread.line !== null) ||
|
|
@@ -98,6 +101,7 @@ export async function handleFixCode(ctx) {
|
|
|
98
101
|
const retryableActionableThreads = mutationActionableThreads.filter((thread) => thread.path !== null && thread.line !== null);
|
|
99
102
|
const protectedRuns = [];
|
|
100
103
|
const stored = await readFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber });
|
|
104
|
+
await aliasThreadFixAttempts({ owner: repoOwner, name: repoName }, prNumber, retryableActionableThreads.map(({ id }) => id), stored);
|
|
101
105
|
const countFixCodeAttempt = opts.persistSeen !== false;
|
|
102
106
|
const priorThreadAttempts = previousFixAttempts(stored, retryableActionableThreads);
|
|
103
107
|
const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, retryableActionableThreads, countFixCodeAttempt);
|
|
@@ -154,7 +158,7 @@ export async function handleFixCode(ctx) {
|
|
|
154
158
|
const threads = report.threads.actionable.map(toAgentThread);
|
|
155
159
|
const resolutionOnlyThreads = report.threads.resolutionOnly;
|
|
156
160
|
const actionableComments = report.comments.actionable.map(toAgentComment);
|
|
157
|
-
const rerunAuthorized = canRerunWorkflows(report.viewerAuthorization);
|
|
161
|
+
const rerunAuthorized = canRerunWorkflows(report.viewerAuthorization, report.transport);
|
|
158
162
|
// A workflow run can only be rerun once it has fully completed; a runId still present among
|
|
159
163
|
// in-progress checks (a sibling job from the same run) is not yet eligible.
|
|
160
164
|
const inProgressWorkflowRunIds = new Set(report.checks.inProgress.flatMap((c) => (c.runId !== null ? [c.runId] : [])));
|
|
@@ -192,7 +196,7 @@ export async function handleFixCode(ctx) {
|
|
|
192
196
|
const { changesRequestedReviews } = report;
|
|
193
197
|
const hasConflicts = report.mergeStatus.status === "CONFLICTS";
|
|
194
198
|
const isBehind = report.mergeStatus.status === "BEHIND";
|
|
195
|
-
const { behindBaseHint } = loadConfig().iterate;
|
|
199
|
+
const { behindBaseHint, instructions: configuredInstructions } = loadConfig().iterate;
|
|
196
200
|
// Only surface in-progress runs when a push is plausible — resolution-only and
|
|
197
201
|
// summary-only iterations have no path to a push, so listing runs would prompt
|
|
198
202
|
// unnecessary cancellation.
|
|
@@ -315,8 +319,12 @@ export async function handleFixCode(ctx) {
|
|
|
315
319
|
bottomPr: report.stackBottomPr,
|
|
316
320
|
})
|
|
317
321
|
: undefined;
|
|
318
|
-
const
|
|
319
|
-
const
|
|
322
|
+
const recovery = buildRemovedQueueRecovery(report, failingAgentChecks, opts.merge);
|
|
323
|
+
const requeue = recovery && !("unavailable" in recovery) ? recovery : undefined;
|
|
324
|
+
const acknowledgmentRecovery = buildStackQueueRemovalAcknowledgment(report, failingAgentChecks);
|
|
325
|
+
const queueRemovalAcknowledgment = acknowledgmentRecovery && !("unavailable" in acknowledgmentRecovery)
|
|
326
|
+
? acknowledgmentRecovery
|
|
327
|
+
: undefined;
|
|
320
328
|
const queueEjection = !currentEjectionCommit(report, failingAgentChecks)
|
|
321
329
|
? undefined
|
|
322
330
|
: requeue
|
|
@@ -329,7 +337,11 @@ export async function handleFixCode(ctx) {
|
|
|
329
337
|
const stackRebase = hasConflicts || (isBehind && exhaustedAttempts.length > 0) || queueEjection
|
|
330
338
|
? buildNativeStackLayerRebase(report.repo, { number: prNumber, baseBranch: baseLookup.branch }, stack, trunkConflict, report.stackBottomPr)
|
|
331
339
|
: undefined;
|
|
332
|
-
const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, cancelled.length, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, inProgressRunIds, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, report.viewerAuthorization?.viewerCanUpdate
|
|
340
|
+
const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, cancelled.length, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, inProgressRunIds, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready"), exhaustedAttempts.length > 0, stackRebase, queueEjection, opts.instructions ?? configuredInstructions);
|
|
341
|
+
if (recovery && "unavailable" in recovery)
|
|
342
|
+
instructions.unshift(recovery.unavailable);
|
|
343
|
+
if (acknowledgmentRecovery && "unavailable" in acknowledgmentRecovery)
|
|
344
|
+
instructions.unshift(acknowledgmentRecovery.unavailable);
|
|
333
345
|
if (failingAgentChecks.some((check) => releasedCheckNames.has(check.name))) {
|
|
334
346
|
const completion = instructions.pop();
|
|
335
347
|
instructions.push(buildReleasedBlockerInstruction(prNumber));
|
|
@@ -340,7 +352,11 @@ export async function handleFixCode(ctx) {
|
|
|
340
352
|
instructions.unshift(...repairInstructions);
|
|
341
353
|
}
|
|
342
354
|
if (mergedBasePullRequests.length > 0) {
|
|
343
|
-
|
|
355
|
+
// `gh pr edit` goes through GraphQL; REST callers retarget with the pulls PATCH endpoint.
|
|
356
|
+
const retarget = report.transport === "rest"
|
|
357
|
+
? `gh api --method PATCH repos/${report.repo}/pulls/${prNumber} -f base=<verified-parent-base>`
|
|
358
|
+
: `gh pr edit ${prReference} --base <verified-parent-base>`;
|
|
359
|
+
instructions.unshift(`Inspect every PR under \`## Merged PRs matching the current base\`. If this PR is the remaining layer intended for a merged parent's base branch, run \`${retarget}\` after replacing \`<verified-parent-base>\` with that parent's shell-quoted base branch, then rerun Shepherd immediately and follow its fresh instructions instead of the remaining steps here. Otherwise keep the current base and follow the remaining conflict-resolution steps.`);
|
|
344
360
|
}
|
|
345
361
|
const checkRunCount = countReportedChecks(report.checks);
|
|
346
362
|
const suitesEmpty = report.headCheckSuitesEmpty === true;
|
|
@@ -390,3 +406,4 @@ export async function handleFixCode(ctx) {
|
|
|
390
406
|
}
|
|
391
407
|
return result;
|
|
392
408
|
}
|
|
409
|
+
import { aliasThreadFixAttempts } from "../../github/rest-identities.mjs";
|
|
@@ -4,11 +4,3 @@ export declare function partitionFixThreads(threads: AgentThread[], resolveComma
|
|
|
4
4
|
unlocatedMutatedThreads: AgentThread[];
|
|
5
5
|
unlocatedThreads: AgentThread[];
|
|
6
6
|
};
|
|
7
|
-
export declare function reviewSectionRefs(input: {
|
|
8
|
-
hasReviewThreads: boolean;
|
|
9
|
-
hasUnlocatedSkipThreads: boolean;
|
|
10
|
-
hasActionableComments: boolean;
|
|
11
|
-
hasFailingChecks: boolean;
|
|
12
|
-
hasAnnotations: boolean;
|
|
13
|
-
hasChangesRequested: boolean;
|
|
14
|
-
}): string[];
|
|
@@ -14,19 +14,3 @@ export function partitionFixThreads(threads, resolveCommand, resolveOnlyCommand)
|
|
|
14
14
|
unlocatedThreads: threads.filter((thread) => (thread.path === null || thread.line === null) && !mutatedThreadIds.has(thread.id)),
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
export function reviewSectionRefs(input) {
|
|
18
|
-
const sections = [];
|
|
19
|
-
if (input.hasReviewThreads)
|
|
20
|
-
sections.push("`## Review threads`");
|
|
21
|
-
if (input.hasUnlocatedSkipThreads)
|
|
22
|
-
sections.push("`## Unlocated review threads (logged once — no mutation)`");
|
|
23
|
-
if (input.hasActionableComments)
|
|
24
|
-
sections.push("`## Actionable comments`");
|
|
25
|
-
if (input.hasFailingChecks)
|
|
26
|
-
sections.push("`## Failing checks`");
|
|
27
|
-
if (input.hasAnnotations)
|
|
28
|
-
sections.push("`## Check annotations`");
|
|
29
|
-
if (input.hasChangesRequested)
|
|
30
|
-
sections.push("`## Changes-requested reviews`");
|
|
31
|
-
return sections;
|
|
32
|
-
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InstructionStyle } from "../../config/load.mts";
|
|
2
|
+
/**
|
|
3
|
+
* The generic commit/push step and the journal step. Playbook style folds both into one pointer
|
|
4
|
+
* only when the generic push applies: the "Fix-code loop" playbook tells the caller to push to the
|
|
5
|
+
* PR head branch, which would conflict with a conflict or queue-recovery push step. Without the
|
|
6
|
+
* generic push, the journal step stays inline with its citation clause. The rerun
|
|
7
|
+
* step stays inline because it is the one step a halted loop cannot recover from.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildPushJournalSteps(style: InstructionStyle, prReference: string | number, genericPush: boolean, wantsJournal: boolean): string[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { renderShellCommand } from "../../cli/runner.mjs";
|
|
2
|
+
import { loadConfig } from "../../config/load.mjs";
|
|
3
|
+
import { buildShepherdJournalCommand, buildShepherdJournalInstruction, } from "../shepherd-journal.mjs";
|
|
4
|
+
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
5
|
+
const FIX_CODE_LOOP_PLAYBOOK = "Fix-code loop";
|
|
6
|
+
/**
|
|
7
|
+
* Playbook-style replacement for the invariant commit/push and journal steps. The
|
|
8
|
+
* procedure text lives in the "Fix-code loop" playbook; only the trigger and the concrete
|
|
9
|
+
* journal command stay in the per-tick output. `inline` style never calls this.
|
|
10
|
+
*/
|
|
11
|
+
function buildFixLoopInstruction(prReference, journal) {
|
|
12
|
+
const parts = ["Commit and push any changes."];
|
|
13
|
+
if (journal)
|
|
14
|
+
parts.push(`Journal substantial decisions or rejections with \`${buildShepherdJournalCommand(prReference)}\`.`);
|
|
15
|
+
const cli = renderShellCommand([...loadConfig().cliCommand, "playbook", FIX_CODE_LOOP_PLAYBOOK]);
|
|
16
|
+
parts.push(`${playbookPointer(FIX_CODE_LOOP_PLAYBOOK)} CLI: \`${cli}\`.`);
|
|
17
|
+
return parts.join(" ");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The generic commit/push step and the journal step. Playbook style folds both into one pointer
|
|
21
|
+
* only when the generic push applies: the "Fix-code loop" playbook tells the caller to push to the
|
|
22
|
+
* PR head branch, which would conflict with a conflict or queue-recovery push step. Without the
|
|
23
|
+
* generic push, the journal step stays inline with its citation clause. The rerun
|
|
24
|
+
* step stays inline because it is the one step a halted loop cannot recover from.
|
|
25
|
+
*/
|
|
26
|
+
export function buildPushJournalSteps(style, prReference, genericPush, wantsJournal) {
|
|
27
|
+
if (style === "playbook" && genericPush)
|
|
28
|
+
return [buildFixLoopInstruction(prReference, wantsJournal)];
|
|
29
|
+
const steps = [];
|
|
30
|
+
if (genericPush)
|
|
31
|
+
steps.push("Commit and push any changes.");
|
|
32
|
+
if (wantsJournal)
|
|
33
|
+
steps.push(buildShepherdJournalInstruction(prReference));
|
|
34
|
+
return steps;
|
|
35
|
+
}
|
|
@@ -23,6 +23,10 @@ export function buildTerminalCancelResult(report) {
|
|
|
23
23
|
return {
|
|
24
24
|
pr: report.pr,
|
|
25
25
|
repo: report.repo,
|
|
26
|
+
...(report.transport && { transport: report.transport }),
|
|
27
|
+
...(report.transportUnavailable?.length && {
|
|
28
|
+
transportUnavailable: report.transportUnavailable,
|
|
29
|
+
}),
|
|
26
30
|
status: report.status,
|
|
27
31
|
mergeStateStatus: report.mergeStatus.mergeStateStatus,
|
|
28
32
|
mergeStatus: report.mergeStatus.status,
|
|
@@ -103,7 +107,12 @@ export function buildActiveChecks(report) {
|
|
|
103
107
|
}
|
|
104
108
|
export function buildWaitLog(base) {
|
|
105
109
|
const { summary, remainingSeconds } = base;
|
|
106
|
-
|
|
110
|
+
// Zero counts are trivial defaults; the line keeps whichever count is non-zero.
|
|
111
|
+
const counts = [
|
|
112
|
+
...(summary.passing > 0 ? [`${summary.passing} passing`] : []),
|
|
113
|
+
...(summary.inProgress > 0 ? [`${summary.inProgress} in-progress`] : []),
|
|
114
|
+
];
|
|
115
|
+
const parts = counts.length > 0 ? [counts.join(", ")] : [];
|
|
107
116
|
if ((base.inProgressChecks?.length ?? 0) > 0) {
|
|
108
117
|
parts.push(`active checks: ${base
|
|
109
118
|
.inProgressChecks.slice(0, 5)
|
|
@@ -133,7 +142,11 @@ export function buildWaitLog(base) {
|
|
|
133
142
|
if (remainingSeconds > 0) {
|
|
134
143
|
parts.push(`${remainingSeconds}s until auto-cancel`);
|
|
135
144
|
}
|
|
136
|
-
|
|
145
|
+
if (parts.length > 0)
|
|
146
|
+
return `WAIT: ${parts.join(" — ")}`;
|
|
147
|
+
// Skipped, filtered, or superseded checks still count as reported CI data.
|
|
148
|
+
const reported = summary.skipped + summary.filtered + summary.superseded > 0;
|
|
149
|
+
return `WAIT: ${reported ? "no passing or in-progress checks" : "no checks reported"}`;
|
|
137
150
|
}
|
|
138
151
|
export function blockedCancelNote(base) {
|
|
139
152
|
if (base.mergeStatus !== "BLOCKED")
|
|
@@ -28,6 +28,8 @@ import { findStaleNativeStackAncestry } from "./stale-ancestry.mjs";
|
|
|
28
28
|
import { annotateBlockedWait, resolveCheckBlockerGate } from "./check-blocker-gate.mjs";
|
|
29
29
|
import { buildUnreportedFixResult, planUnreportedRequired } from "./unreported-required.mjs";
|
|
30
30
|
import { createCheckExecutionContext } from "../check-execution-context.mjs";
|
|
31
|
+
import { transportReadinessGaps } from "../../github/transport-evidence.mjs";
|
|
32
|
+
import { buildEscalateHumanMessage } from "./escalate.mjs";
|
|
31
33
|
export function runIterate(opts) {
|
|
32
34
|
return withIterateApiUsage(opts, () => runIterateCore(opts));
|
|
33
35
|
}
|
|
@@ -194,7 +196,7 @@ async function runIterateCore(opts) {
|
|
|
194
196
|
});
|
|
195
197
|
}
|
|
196
198
|
if (unreportedRepair.length > 0) {
|
|
197
|
-
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, buildUnreportedFixResult(base, report, unreportedRepair), reportForWork, reviewSummaryIds), blockerGate);
|
|
199
|
+
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, buildUnreportedFixResult(base, report, unreportedRepair), reportForWork, reviewSummaryIds, opts.stallDeadlineSink), blockerGate);
|
|
198
200
|
}
|
|
199
201
|
const mergeStateResult = await handleActiveMergeState({
|
|
200
202
|
enabled: opts.merge,
|
|
@@ -234,23 +236,49 @@ async function runIterateCore(opts) {
|
|
|
234
236
|
const canMarkReady = report.status === "READY" &&
|
|
235
237
|
report.mergeStatus.isDraft &&
|
|
236
238
|
!report.mergeStatus.blockingBotReviewInProgress;
|
|
239
|
+
const gaps = transportReadinessGaps(report, report.mergeStatus.mergeStateStatus);
|
|
240
|
+
if (gaps.length > 0 &&
|
|
241
|
+
report.checks.inProgress.length === 0 &&
|
|
242
|
+
report.mergeStatus.mergeStateStatus !== "UNKNOWN") {
|
|
243
|
+
await clearReadyReceipt(receiptKey);
|
|
244
|
+
const escalation = {
|
|
245
|
+
triggers: ["transport-unsupported"],
|
|
246
|
+
unresolvedThreads: [],
|
|
247
|
+
ambiguousComments: [],
|
|
248
|
+
changesRequestedReviews: [],
|
|
249
|
+
suggestion: gaps.map(({ field, reason }) => `${field}: ${reason}`).join("; "),
|
|
250
|
+
};
|
|
251
|
+
return {
|
|
252
|
+
...base,
|
|
253
|
+
action: "escalate",
|
|
254
|
+
escalate: { ...escalation, humanMessage: buildEscalateHumanMessage(escalation, report.pr) },
|
|
255
|
+
};
|
|
256
|
+
}
|
|
237
257
|
const autoMarkReady = !opts.noAutoMarkReady && config.actions.autoMarkReady;
|
|
238
258
|
const markReadyResult = await markReadyIfAuthorized(canMarkReady && autoMarkReady, base, report);
|
|
239
259
|
if (markReadyResult)
|
|
240
260
|
return markReadyResult;
|
|
241
261
|
if (readyState.shouldCancel && !report.mergeStatus.isDraft) {
|
|
242
|
-
const
|
|
262
|
+
const receipt = receiptCurrent
|
|
263
|
+
? "written"
|
|
264
|
+
: await recordReadyReceipt(receiptKey, report, checkContext);
|
|
243
265
|
// Aggregate --stack routing trusts only a layer's receipt, so an unwritten
|
|
244
266
|
// one keeps the elapsed marker and retries next tick. A one-PR receipt
|
|
245
267
|
// only lets a rerun skip the wait, so its failure never changes the action.
|
|
246
|
-
|
|
268
|
+
// A layer GitHub reports BLOCKED gets no receipt, so aggregate routing
|
|
269
|
+
// still holds it, but without --merge its one-PR session ends like a
|
|
270
|
+
// single PR's instead of waiting for a stall-timeout.
|
|
271
|
+
const stackLayer = report.mergeStatus.mergeRequirements?.stack !== undefined;
|
|
272
|
+
if (stackLayer && (receipt === "failed" || (receipt === "blocked" && opts.merge))) {
|
|
247
273
|
const receiptWait = {
|
|
248
274
|
...base,
|
|
249
275
|
action: "wait",
|
|
250
276
|
shouldCancel: false,
|
|
251
|
-
log:
|
|
277
|
+
log: receipt === "blocked"
|
|
278
|
+
? `WAIT: PR #${base.pr} reached ready-delay but GitHub reports BLOCKED, so its stack readiness receipt was not certified`
|
|
279
|
+
: `WAIT: PR #${base.pr} reached ready-delay but its stack readiness receipt could not be persisted`,
|
|
252
280
|
};
|
|
253
|
-
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, receiptWait, reportForWork, reviewSummaryIds), blockerGate);
|
|
281
|
+
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, receiptWait, reportForWork, reviewSummaryIds, opts.stallDeadlineSink), blockerGate);
|
|
254
282
|
}
|
|
255
283
|
await clearReadyDelay(report.pr, repoOwner, repoName);
|
|
256
284
|
await clearStallState(stallKey);
|
|
@@ -283,15 +311,21 @@ async function runIterateCore(opts) {
|
|
|
283
311
|
log: buildWaitLog(base),
|
|
284
312
|
...(hold && { stackDraftHold: hold }),
|
|
285
313
|
};
|
|
286
|
-
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, wait, reportForWork, reviewSummaryIds), blockerGate);
|
|
314
|
+
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, wait, reportForWork, reviewSummaryIds, opts.stallDeadlineSink), blockerGate);
|
|
287
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Persist a READY receipt from a fresh compact snapshot. `blocked` means the
|
|
318
|
+
* snapshot is ready except for GitHub's BLOCKED state; `failed` covers missing,
|
|
319
|
+
* stale, or not-ready evidence and write errors.
|
|
320
|
+
*/
|
|
288
321
|
async function recordReadyReceipt(key, report, context) {
|
|
289
322
|
if (report.status !== "READY" ||
|
|
290
323
|
report.mergeStatus.state !== "OPEN" ||
|
|
291
324
|
report.mergeStatus.isDraft ||
|
|
292
325
|
!report.headSha ||
|
|
293
|
-
!report.baseRefOid
|
|
294
|
-
|
|
326
|
+
!report.baseRefOid ||
|
|
327
|
+
transportReadinessGaps(report, report.mergeStatus.mergeStateStatus).length > 0)
|
|
328
|
+
return "failed";
|
|
295
329
|
try {
|
|
296
330
|
const raw = await receiptSummaryForReport(key, report, context);
|
|
297
331
|
if (fingerprintRawSummaryPr(raw) === null ||
|
|
@@ -299,16 +333,22 @@ async function recordReadyReceipt(key, report, context) {
|
|
|
299
333
|
raw.isDraft ||
|
|
300
334
|
raw.headRefOid !== report.headSha ||
|
|
301
335
|
raw.baseRefOid !== report.baseRefOid)
|
|
302
|
-
return
|
|
336
|
+
return "failed";
|
|
303
337
|
// Summarize hydrates annotation totals onto the snapshot before the stored hash.
|
|
304
338
|
const summary = await summarizePollSummaryPr(raw, { owner: key.owner, name: key.repo }, { stackPrNumber: report.pr });
|
|
305
339
|
if (annotationProbeUnavailable(raw))
|
|
306
|
-
return
|
|
340
|
+
return "failed";
|
|
307
341
|
const fingerprint = fingerprintRawSummaryPr(raw);
|
|
308
342
|
if (fingerprint === null)
|
|
309
|
-
return
|
|
310
|
-
|
|
311
|
-
|
|
343
|
+
return "failed";
|
|
344
|
+
const checks = summary.checks ?? {};
|
|
345
|
+
const review = summary.review ?? {};
|
|
346
|
+
if (!isCurrentSummaryReady(raw, checks, review)) {
|
|
347
|
+
const onlyBlocked = raw.mergeStateStatus === "BLOCKED" &&
|
|
348
|
+
!raw.isInMergeQueue &&
|
|
349
|
+
isCurrentSummaryReady(raw, checks, review, { allowBlocked: true });
|
|
350
|
+
return onlyBlocked ? "blocked" : "failed";
|
|
351
|
+
}
|
|
312
352
|
const removalEvent = currentQueueRemovalEvent(raw);
|
|
313
353
|
// Bind recovery to the exact evidence the one-PR check accepted. A new ejection
|
|
314
354
|
// between that check and this fresh snapshot must get its own acknowledgment.
|
|
@@ -320,7 +360,7 @@ async function recordReadyReceipt(key, report, context) {
|
|
|
320
360
|
removalEvent.beforeCommit?.oid !== accepted.beforeCommitOid ||
|
|
321
361
|
Math.floor(Date.parse(removalEvent.createdAt) / 1000) !== accepted.createdAtUnix ||
|
|
322
362
|
removalEvent.reason !== accepted.reason)
|
|
323
|
-
return
|
|
363
|
+
return "failed";
|
|
324
364
|
}
|
|
325
365
|
await writeReadyReceipt({
|
|
326
366
|
version: 1,
|
|
@@ -335,13 +375,13 @@ async function recordReadyReceipt(key, report, context) {
|
|
|
335
375
|
}),
|
|
336
376
|
recordedAtUnix: Math.floor(Date.now() / 1000),
|
|
337
377
|
});
|
|
338
|
-
return
|
|
378
|
+
return "written";
|
|
339
379
|
}
|
|
340
380
|
catch {
|
|
341
381
|
// The receipt is supplementary evidence. A transient summary fetch or
|
|
342
382
|
// state-directory failure must not turn a completed one-PR poll into a
|
|
343
383
|
// different Shepherd action.
|
|
344
|
-
return
|
|
384
|
+
return "failed";
|
|
345
385
|
}
|
|
346
386
|
}
|
|
347
387
|
/** Clear a stale READY receipt; return whether a current receipt remains. */
|
|
@@ -357,6 +397,7 @@ async function revalidateReadyReceipt(key, report, hasReadinessWork, context) {
|
|
|
357
397
|
report.mergeStatus.isDraft ||
|
|
358
398
|
!report.headSha ||
|
|
359
399
|
!report.baseRefOid ||
|
|
400
|
+
transportReadinessGaps(report, report.mergeStatus.mergeStateStatus).length > 0 ||
|
|
360
401
|
(!retainQueuedReceipt && (report.status !== "READY" || hasReadinessWork))) {
|
|
361
402
|
await clearReadyReceipt(key);
|
|
362
403
|
return false;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { graphql } from "../../github/http.mjs";
|
|
2
2
|
import { MARK_PR_READY_MUTATION } from "../../github/queries.mjs";
|
|
3
3
|
import { buildEscalateHumanMessage, buildEscalateSuggestion } from "./escalate.mjs";
|
|
4
|
+
import { rest } from "../../github/http.mjs";
|
|
5
|
+
import { githubOperation, isCcrTransport } from "../../github/transport.mjs";
|
|
6
|
+
import { canGenerateGithubMutation } from "../../github/mutation-policy.mjs";
|
|
7
|
+
import { UnsupportedRestOperationError } from "../../github/unsupported-rest.mjs";
|
|
8
|
+
import { GitHubRequestError } from "../../github/errors.mjs";
|
|
9
|
+
import { isRestSessionRefusal } from "../../github/rest-session-refusal.mjs";
|
|
10
|
+
import { rateLimitKind } from "../../github/rate-limit-kind.mjs";
|
|
11
|
+
import { nonDraftMergeStatus } from "../../merge-status/derive.mjs";
|
|
4
12
|
export async function markReadyIfAuthorized(enabled, base, report) {
|
|
5
13
|
if (!enabled)
|
|
6
14
|
return null;
|
|
@@ -12,17 +20,49 @@ export async function markReadyIfAuthorized(enabled, base, report) {
|
|
|
12
20
|
log: `READY: PR #${report.pr} is ready to mark; refresh required before converting draft`,
|
|
13
21
|
};
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
let unsupported;
|
|
24
|
+
if (canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready")) {
|
|
25
|
+
try {
|
|
26
|
+
await githubOperation("MarkPrReady", async () => {
|
|
27
|
+
await graphql(MARK_PR_READY_MUTATION, { pullRequestId: report.nodeId });
|
|
28
|
+
}, async () => {
|
|
29
|
+
if (!isCcrTransport())
|
|
30
|
+
throw new UnsupportedRestOperationError("markPullRequestReadyForReview", "ready-for-review requires the cloud CCR proxy");
|
|
31
|
+
const response = await rest("POST", `/repos/${report.repo}/pulls/${report.pr}/ccr/ready_for_review`);
|
|
32
|
+
if (response?.draft !== false)
|
|
33
|
+
throw new Error("CCR ready_for_review did not confirm draft:false; refresh the PR before retrying");
|
|
34
|
+
}, { mutation: true });
|
|
35
|
+
// The snapshot predates the conversion; GitHub just confirmed the PR is no longer a draft.
|
|
36
|
+
// A raw DRAFT merge state has not been recomputed yet, so it becomes UNKNOWN.
|
|
37
|
+
const mergeStateStatus = base.mergeStateStatus === "DRAFT" ? "UNKNOWN" : base.mergeStateStatus;
|
|
38
|
+
return {
|
|
39
|
+
...base,
|
|
40
|
+
isDraft: false,
|
|
41
|
+
mergeStateStatus,
|
|
42
|
+
...(base.mergeStatus === "DRAFT" && { mergeStatus: nonDraftMergeStatus(mergeStateStatus) }),
|
|
43
|
+
action: "mark_ready",
|
|
44
|
+
markedReady: true,
|
|
45
|
+
log: `MARKED READY: PR #${report.pr} converted from draft to ready for review`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof UnsupportedRestOperationError ||
|
|
50
|
+
isRestSessionRefusal(error) ||
|
|
51
|
+
(error instanceof GitHubRequestError && error.status === 404))
|
|
52
|
+
unsupported = error.message;
|
|
53
|
+
else if (!(error instanceof GitHubRequestError) ||
|
|
54
|
+
error.status !== 403 ||
|
|
55
|
+
rateLimitKind(error) !== null)
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (report.transport === "rest" && report.viewerAuthorization?.viewerCanUpdate !== false) {
|
|
60
|
+
unsupported = "Ready-for-review requires the cloud CCR proxy";
|
|
23
61
|
}
|
|
24
62
|
const escalation = {
|
|
25
|
-
triggers: [
|
|
63
|
+
triggers: [
|
|
64
|
+
unsupported ? "transport-unsupported" : "authorization-required",
|
|
65
|
+
],
|
|
26
66
|
unresolvedThreads: [],
|
|
27
67
|
ambiguousComments: [],
|
|
28
68
|
changesRequestedReviews: [],
|
|
@@ -33,7 +73,7 @@ export async function markReadyIfAuthorized(enabled, base, report) {
|
|
|
33
73
|
reason: "denied-or-unverifiable",
|
|
34
74
|
},
|
|
35
75
|
],
|
|
36
|
-
suggestion: buildEscalateSuggestion(["authorization-required"]),
|
|
76
|
+
suggestion: unsupported ?? buildEscalateSuggestion(["authorization-required"]),
|
|
37
77
|
};
|
|
38
78
|
return {
|
|
39
79
|
...base,
|