@joshuafolkken/kit 1.1784.0 → 1.2116.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/agents/reviewer-verification.md +19 -0
- package/.claude/agents/reviewer.md +19 -0
- package/.claude/settings.json +5 -6
- package/.claude/skills/dependency-update/SKILL.md +9 -10
- package/.claude/skills/workflow-commands/SKILL.md +6 -6
- package/.claude/skills/workflow-commands/background-commands.md +8 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +41 -32
- package/.claude/skills/workflow-commands/delegation.md +6 -6
- package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +7 -5
- package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +9 -4
- package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -2
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
- package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
- package/.claude/skills/workflow-commands/prerequisite.md +3 -2
- package/.claude/skills/workflow-commands/retrospective.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +7 -5
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-pnpm/action.yml +9 -16
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/auto-tag.yml +45 -0
- package/.github/workflows/ci.yml +155 -15
- package/.github/workflows/dependabot-auto-merge.yml +63 -4
- package/.github/workflows/production.yml +8 -0
- package/.github/workflows/publish.yml +60 -45
- package/.vscode/settings.json +19 -0
- package/CLAUDE.md +5 -12
- package/SECURITY.md +2 -2
- package/dist/CLAUDE.md +5 -12
- package/dist/commands/document-read-cli.js +2 -1
- package/dist/commands/document-section-main.js +2 -1
- package/dist/commands/read-set-cli.js +194 -134
- package/dist/commands/run-prep-main.js +9694 -10173
- package/dist/commands/run-step-cli.js +22138 -18285
- package/dist/hooks/audit-provision.js +23 -5
- package/dist/hooks/chunk-3GKE653K.js +6204 -0
- package/dist/hooks/chunk-45CJJOO7.js +1243 -0
- package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
- package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
- package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
- package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
- package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
- package/dist/hooks/chunk-VVNYJWEI.js +30 -0
- package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
- package/dist/hooks/codex-hook-adapter.js +8 -8
- package/dist/hooks/format-edited.js +4 -5
- package/dist/hooks/inputs.json +386 -360
- package/dist/hooks/pretool-guard.js +7 -6
- package/dist/hooks/session-lang.js +101 -258
- package/dist/hooks/stop-guard.js +1744 -2576
- package/dist/josh.js +7133 -6368
- package/dist/version/index.js +161 -1
- package/eslint/rules/comment-why-only.js +108 -0
- package/package.json +16 -9
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +14 -11
- package/prompts/collaboration-workflow/issue-citation.md +0 -4
- package/prompts/collaboration-workflow/issue-template.md +40 -21
- package/prompts/collaboration-workflow/lane-instructions.md +3 -1
- package/prompts/collaboration-workflow/measurement.md +8 -1
- package/prompts/collaboration-workflow/operating-rules.md +11 -2
- package/prompts/collaboration-workflow/output-bounds.md +6 -2
- package/prompts/collaboration-workflow/plan-comment.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/residency.md +3 -2
- package/prompts/collaboration-workflow/rule-delivery.md +13 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +3 -1
- package/prompts/review-rubric.md +8 -0
- package/prompts/review.md +31 -22
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-argv.ts +14 -16
- package/scripts/agent/agent-provider.ts +138 -0
- package/scripts/agent/agent-role-profile.ts +108 -156
- package/scripts/agent/agent-safe-text.ts +20 -5
- package/scripts/agent/api-usage-limit.ts +65 -0
- package/scripts/agent/claude-agent-argv.ts +19 -9
- package/scripts/agent/codex-agent-argv.ts +118 -22
- package/scripts/agent/usage-limit-reset.ts +179 -0
- package/scripts/auth/claude-token-output.ts +2 -2
- package/scripts/auto-ok/auto-ok-author.ts +53 -0
- package/scripts/auto-ok/auto-ok-cli.ts +20 -9
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +6 -1
- package/scripts/backlog/backlog-drive-hold.ts +3 -3
- package/scripts/backlog/backlog-drive-idle.ts +10 -0
- package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
- package/scripts/backlog/backlog-drive-launch.ts +8 -2
- package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
- package/scripts/backlog/backlog-drive-named.ts +28 -7
- package/scripts/backlog/backlog-drive-paused.ts +5 -4
- package/scripts/backlog/backlog-drive-restore.ts +11 -3
- package/scripts/backlog/backlog-drive.ts +21 -60
- package/scripts/backlog/backlog-gate-scope.ts +61 -0
- package/scripts/backlog/backlog-measure-close.ts +112 -0
- package/scripts/backlog/backlog-next.ts +39 -49
- package/scripts/backlog/backlog-occupied.ts +2 -2
- package/scripts/backlog/backlog-overlap.ts +42 -8
- package/scripts/backlog/backlog-plan-cli.ts +4 -1
- package/scripts/backlog/backlog-plan-comment.ts +81 -0
- package/scripts/backlog/backlog-plan-read.ts +19 -6
- package/scripts/backlog/backlog-pool.ts +50 -12
- package/scripts/backlog/backlog-rank.ts +60 -13
- package/scripts/backlog/backlog-stalled-detect.ts +2 -1
- package/scripts/backlog/backlog-waves.ts +17 -2
- package/scripts/backlog/backlogrun-launch.ts +7 -4
- package/scripts/cost-runtime/context-cut-payback.ts +27 -25
- package/scripts/cost-runtime/cost-cli.ts +37 -16
- package/scripts/cost-runtime/cost-pricing.ts +9 -0
- package/scripts/cost-runtime/cost-verdict.ts +4 -1
- package/scripts/delegation/delegation-policy.ts +2 -2
- package/scripts/delegation/duplicate-read-guard.ts +1 -0
- package/scripts/delegation/investigation-guard.ts +14 -2
- package/scripts/delegation/investigation-reads.ts +45 -23
- package/scripts/dependabot/dependabot-cooldown.ts +3 -2
- package/scripts/discovery/repo-party.ts +80 -20
- package/scripts/discovery/session-owner-pin.ts +51 -0
- package/scripts/document/capped-print.ts +1 -1
- package/scripts/document/command-reference-line-budget.ts +42 -0
- package/scripts/document/document-byte-budget.ts +17 -7
- package/scripts/document/document-checks.ts +20 -0
- package/scripts/document/document-ratchet-check.ts +101 -0
- package/scripts/document/document-section.ts +1 -0
- package/scripts/document/entry-read-budget.ts +7 -3
- package/scripts/document/issue-citation-budget.ts +94 -0
- package/scripts/epic/epic-cli-argv.ts +2 -2
- package/scripts/epic/epic-fetch.ts +4 -2
- package/scripts/epic/epic-graph.ts +7 -2
- package/scripts/epic/epic-issue.ts +3 -1
- package/scripts/epic/epic-rank.ts +5 -0
- package/scripts/exports/unused-members-check.ts +62 -0
- package/scripts/exports/unused-members-cli.ts +6 -51
- package/scripts/followup/followup-ci-path.ts +67 -0
- package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
- package/scripts/followup/followup-coderabbit-wait.ts +13 -10
- package/scripts/followup/followup-failed-tests.ts +160 -0
- package/scripts/followup/followup-job-needs.ts +100 -0
- package/scripts/followup/followup-job-rerun.ts +303 -0
- package/scripts/followup/followup-main-move.ts +113 -0
- package/scripts/followup/git-followup-finish.ts +43 -11
- package/scripts/followup/git-followup-pending.ts +3 -3
- package/scripts/followup/git-followup-stages.ts +41 -11
- package/scripts/followup/git-pr-followup-checks.ts +55 -16
- package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
- package/scripts/followup/git-pr-followup.ts +62 -27
- package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
- package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
- package/scripts/gate/busy-average.ts +34 -2
- package/scripts/gate/check-related-pinned.ts +43 -0
- package/scripts/gate/check-related-reuse.ts +64 -0
- package/scripts/gate/check-related-section.ts +57 -0
- package/scripts/gate/check-related-unplanned.ts +34 -0
- package/scripts/gate/check-related.ts +265 -0
- package/scripts/gate/command-memory.ts +1 -1
- package/scripts/gate/core-admission.ts +24 -6
- package/scripts/gate/core-budget-timing.ts +20 -6
- package/scripts/gate/core-budget.ts +40 -11
- package/scripts/gate/core-wait-record.ts +3 -2
- package/scripts/gate/core-wait.ts +25 -2
- package/scripts/gate/core-weights.ts +15 -7
- package/scripts/gate/gate-labels.ts +46 -0
- package/scripts/gate/gate-ledger.ts +14 -6
- package/scripts/gate/gate-plan.ts +103 -58
- package/scripts/gate/gate-report.ts +9 -7
- package/scripts/gate/gate-run-steps.ts +26 -3
- package/scripts/gate/gate-skip.ts +45 -6
- package/scripts/gate/gate-static-group.ts +60 -0
- package/scripts/gate/gate-steps.ts +2 -1
- package/scripts/gate/gate-tree.ts +4 -2
- package/scripts/gate/hook-gate-reuse.ts +19 -2
- package/scripts/gate/metrics-totals.ts +59 -0
- package/scripts/gate/scoped-green.ts +68 -15
- package/scripts/gate/since-green.ts +14 -6
- package/scripts/gate/structural-checks.ts +96 -0
- package/scripts/gate/verification-gate.ts +51 -16
- package/scripts/gate/verification-inputs.ts +21 -6
- package/scripts/gate/verification-tail.ts +52 -0
- package/scripts/gh/gh-author-association.ts +23 -2
- package/scripts/gh/git-ai-review-scan.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +12 -3
- package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
- package/scripts/gh/git-gh-exec.ts +32 -2
- package/scripts/gh/git-gh-issue-read.ts +4 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +8 -2
- package/scripts/gh/git-gh-issue.ts +12 -5
- package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
- package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
- package/scripts/gh/git-gh-pr-read.ts +74 -13
- package/scripts/gh/git-gh-pr-rest.ts +3 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
- package/scripts/gh/git-gh-pr-too-large.ts +20 -0
- package/scripts/gh/git-gh-pr.ts +21 -7
- package/scripts/gh/git-gh-retry.ts +141 -0
- package/scripts/gh/git-pr-ai-review.ts +15 -6
- package/scripts/gh/git-pr-checks-eval.ts +3 -0
- package/scripts/gh/git-pr-checks.ts +5 -5
- package/scripts/git/base-tree.ts +30 -0
- package/scripts/git/change-base.ts +4 -10
- package/scripts/git/changed-paths-sync.ts +63 -0
- package/scripts/git/changed-paths.ts +16 -1
- package/scripts/git/git-common-directory.ts +11 -1
- package/scripts/git/git-diff-reads.ts +66 -20
- package/scripts/git/git-error.ts +9 -2
- package/scripts/git/git-schemas.ts +13 -0
- package/scripts/git/git-worktree.ts +13 -0
- package/scripts/git/gone-branch.ts +4 -1
- package/scripts/git/main-merge-budget-conflict.ts +89 -0
- package/scripts/git/main-merge-guard.ts +55 -18
- package/scripts/git/main-merge-regenerate.ts +59 -25
- package/scripts/git/main-merge.ts +53 -5
- package/scripts/git/no-base-checkout.ts +127 -0
- package/scripts/git/stash/git-stash.ts +11 -2
- package/scripts/git/stash/stash-push-cli.ts +10 -0
- package/scripts/hooks/batch-guard.ts +40 -21
- package/scripts/hooks/edited-cspell.ts +37 -11
- package/scripts/hooks/format-edited-file.ts +22 -2
- package/scripts/hooks/lane-plan-guard.ts +50 -0
- package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
- package/scripts/hooks/pre-commit-lint.ts +14 -1
- package/scripts/hooks/pre-push-related-switch.ts +10 -0
- package/scripts/hooks/pre-push-unit.ts +76 -10
- package/scripts/hooks/pretool-guard.ts +2 -0
- package/scripts/hooks/reserved-run.ts +14 -1
- package/scripts/hooks/run-hook.sh +23 -8
- package/scripts/hooks/step-zero-notice.ts +6 -1
- package/scripts/hooks/stop-actions.ts +3 -15
- package/scripts/hooks/stop-guard.ts +7 -6
- package/scripts/init/distributed-paths.ts +1 -0
- package/scripts/init/hook-command-rewrite.ts +5 -1
- package/scripts/init/hook-launch.ts +4 -2
- package/scripts/issue/aggregation-split.ts +52 -0
- package/scripts/issue/defect-origin-line.ts +19 -1
- package/scripts/issue/defect-rate-cli.ts +5 -102
- package/scripts/issue/defect-report.ts +4 -4
- package/scripts/issue/effect-audit-cli.ts +178 -0
- package/scripts/issue/effect-audit.ts +185 -0
- package/scripts/issue/expected-effect-lint.ts +64 -9
- package/scripts/issue/git-next-issues.ts +5 -0
- package/scripts/issue/issue-body-or-comment.ts +38 -4
- package/scripts/issue/issue-cite-cli.ts +1 -3
- package/scripts/issue/issue-classification.ts +1 -0
- package/scripts/issue/issue-closing-pr.ts +8 -1
- package/scripts/issue/issue-evidence-exit.ts +4 -2
- package/scripts/issue/issue-file-cap.ts +58 -0
- package/scripts/issue/issue-file-cli.ts +111 -38
- package/scripts/issue/issue-file-deferral.ts +52 -0
- package/scripts/issue/issue-file-fold.ts +29 -10
- package/scripts/issue/issue-file-interrupt.ts +85 -0
- package/scripts/issue/issue-file-lane-hint.ts +30 -0
- package/scripts/issue/issue-file.ts +43 -6
- package/scripts/issue/issue-labels.ts +19 -2
- package/scripts/issue/issue-lint-cli.ts +27 -2
- package/scripts/issue/issue-measured-cli.ts +13 -35
- package/scripts/issue/issue-measured-close.ts +58 -0
- package/scripts/issue/issue-not-before-label.ts +2 -2
- package/scripts/issue/issue-not-before.ts +4 -1
- package/scripts/issue/issue-priority-cli.ts +60 -0
- package/scripts/issue/issue-priority-raise.ts +55 -0
- package/scripts/issue/issue-priority.ts +174 -43
- package/scripts/issue/issue-rank.ts +21 -4
- package/scripts/issue/issue-read-cli.ts +32 -6
- package/scripts/issue/issue-read.ts +37 -12
- package/scripts/issue/issue-sample-gate.ts +95 -74
- package/scripts/issue/issue-sample-prime.ts +127 -0
- package/scripts/issue/issue-sample-required.ts +57 -0
- package/scripts/issue/issue-sample-source.ts +12 -11
- package/scripts/issue/issue-search.ts +124 -0
- package/scripts/issue/issue-state.ts +13 -1
- package/scripts/issue/issue-template-section.ts +43 -0
- package/scripts/issue/issue-watch-diff.ts +31 -1
- package/scripts/issue/issue-watch-hook.ts +11 -2
- package/scripts/issue/issue-watch.ts +55 -12
- package/scripts/issue/markdown-section.ts +1 -0
- package/scripts/issue/measure-declaration.ts +121 -0
- package/scripts/issue/measure-rerun-cli.ts +7 -70
- package/scripts/issue/plan-consumers-section.ts +22 -1
- package/scripts/issue/reproduction-measure.ts +3 -1
- package/scripts/issue/size-estimate.ts +90 -0
- package/scripts/issue/trusted-measure.ts +84 -0
- package/scripts/josh/command-usage.ts +3 -2
- package/scripts/josh/durable-state-migrate.ts +105 -0
- package/scripts/josh/durable-state.ts +112 -0
- package/scripts/josh/josh-aliases.ts +95 -0
- package/scripts/josh/josh-command-map.ts +1 -82
- package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
- package/scripts/josh/josh-commands-ai.ts +5 -5
- package/scripts/josh/josh-commands-development.ts +59 -58
- package/scripts/josh/josh-commands-issue-write.ts +23 -4
- package/scripts/josh/josh-commands-issue.ts +32 -0
- package/scripts/josh/josh-commands-lane.ts +87 -4
- package/scripts/josh/josh-commands-lint.ts +16 -3
- package/scripts/josh/josh-commands-maintenance.ts +15 -0
- package/scripts/josh/josh-commands-review.ts +41 -6
- package/scripts/josh/josh-commands-ship.ts +56 -8
- package/scripts/josh/josh-commands-test-check.ts +80 -0
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/josh-environment-file.ts +43 -3
- package/scripts/josh/josh-in-process.ts +2 -2
- package/scripts/josh/josh-logic.ts +5 -3
- package/scripts/josh/josh-run.ts +19 -1
- package/scripts/josh/run-record-path.ts +15 -0
- package/scripts/josh/stamp-file.ts +11 -2
- package/scripts/lane/lane-adopt-cli.ts +98 -0
- package/scripts/lane/lane-adopt.ts +208 -0
- package/scripts/lane/lane-await.ts +5 -8
- package/scripts/lane/lane-baseline.ts +123 -0
- package/scripts/lane/lane-breakdown-cli.ts +64 -0
- package/scripts/lane/lane-breakdown-implement.ts +170 -0
- package/scripts/lane/lane-breakdown-laps.ts +107 -0
- package/scripts/lane/lane-breakdown-pinned.ts +76 -0
- package/scripts/lane/lane-breakdown-text.ts +145 -0
- package/scripts/lane/lane-breakdown.ts +239 -0
- package/scripts/lane/lane-check-stats.ts +29 -8
- package/scripts/lane/lane-child-invocation.ts +9 -2
- package/scripts/lane/lane-child-mark-keys.ts +12 -0
- package/scripts/lane/lane-child-marker.ts +20 -1
- package/scripts/lane/lane-ci-path-stats.ts +73 -0
- package/scripts/lane/lane-claim-stats.ts +84 -0
- package/scripts/lane/lane-close.ts +8 -3
- package/scripts/lane/lane-dispatch-log.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +2 -2
- package/scripts/lane/lane-gap-stats.ts +7 -6
- package/scripts/lane/lane-guard-policy.ts +7 -1
- package/scripts/lane/lane-implement-stats.ts +207 -0
- package/scripts/lane/lane-implement-subagents.ts +90 -0
- package/scripts/lane/lane-investigation-switch.ts +16 -0
- package/scripts/lane/lane-ledger-kind.ts +31 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
- package/scripts/lane/lane-ledger-schema.ts +119 -36
- package/scripts/lane/lane-ledger-write.ts +257 -0
- package/scripts/lane/lane-ledger.ts +31 -177
- package/scripts/lane/lane-load.ts +1 -1
- package/scripts/lane/lane-measure-cli.ts +176 -59
- package/scripts/lane/lane-net-read.ts +44 -0
- package/scripts/lane/lane-net-time.ts +212 -0
- package/scripts/lane/lane-open.ts +16 -7
- package/scripts/lane/lane-output.ts +2 -4
- package/scripts/lane/lane-overrun-prompt.ts +147 -0
- package/scripts/lane/lane-overrun.ts +274 -0
- package/scripts/lane/lane-paths.ts +24 -1
- package/scripts/lane/lane-pause-cli.ts +4 -1
- package/scripts/lane/lane-paused-spans.ts +97 -0
- package/scripts/lane/lane-plan-check.ts +207 -0
- package/scripts/lane/lane-plan-cli.ts +41 -0
- package/scripts/lane/lane-plan.ts +144 -0
- package/scripts/lane/lane-registry.ts +14 -1
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-round-one-stats.ts +51 -0
- package/scripts/lane/lane-sampler.ts +23 -4
- package/scripts/lane/lane-session-action.ts +93 -18
- package/scripts/lane/lane-session-broker.ts +49 -21
- package/scripts/lane/lane-session-reread-main.ts +14 -0
- package/scripts/lane/lane-session-reread.ts +51 -0
- package/scripts/lane/lane-ship-lap-stats.ts +74 -0
- package/scripts/lane/lane-sleep.ts +58 -0
- package/scripts/lane/lane-spent.ts +1 -1
- package/scripts/lane/lane-split-stats.ts +28 -0
- package/scripts/lane/lane-stage-stats.ts +13 -1
- package/scripts/lane/lane-state.ts +41 -9
- package/scripts/lane/lane-stats-tables.ts +58 -0
- package/scripts/lane/lane-stats.ts +21 -5
- package/scripts/lane/lane-stop-stats.ts +51 -19
- package/scripts/lane/lane-tail-stats.ts +32 -0
- package/scripts/lane/lane-transcript-calls.ts +67 -10
- package/scripts/lane/lane-transcript-stats.ts +44 -12
- package/scripts/lane/lane-value-cell.ts +72 -0
- package/scripts/lane/lane-value.ts +118 -0
- package/scripts/lane/lane-withdraw-closed.ts +124 -0
- package/scripts/lane/lane-withdraw.ts +108 -31
- package/scripts/lane/lane-yield.ts +85 -0
- package/scripts/lane/openai-lane-ship.ts +12 -11
- package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
- package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
- package/scripts/lane/openai-lane-supervisor.ts +5 -4
- package/scripts/lib/poll.ts +38 -9
- package/scripts/lib/settled-together.ts +10 -0
- package/scripts/lib/shell-quote.ts +21 -0
- package/scripts/lib/token-shape.ts +111 -0
- package/scripts/lint/eslint-daemon-sources.ts +79 -0
- package/scripts/lint/eslint-daemon.ts +45 -6
- package/scripts/lint/eslint-warm-cli.ts +7 -0
- package/scripts/lint/eslint-warm.ts +66 -0
- package/scripts/lint/import-graph.ts +141 -11
- package/scripts/lint/lint-gate-scope.ts +2 -2
- package/scripts/lint/lint-parallel.ts +54 -6
- package/scripts/lint/lint-related.ts +154 -33
- package/scripts/lint/rename-cli.ts +179 -0
- package/scripts/lint/rename-identifier.ts +192 -0
- package/scripts/lint/type-only-imports.ts +58 -0
- package/scripts/notify/telegram-notify.ts +12 -3
- package/scripts/observations/observation-record.ts +42 -16
- package/scripts/observations/observations-flush.ts +32 -9
- package/scripts/overrides/overrides-check.ts +9 -1
- package/scripts/release/release-approval-prune.ts +7 -1
- package/scripts/repo/required-checks-logic.ts +4 -1
- package/scripts/repo/ruleset-check.ts +4 -3
- package/scripts/report/markdown-shape.ts +28 -0
- package/scripts/report/report-format-reference.ts +15 -1
- package/scripts/report/report-labels.ts +12 -1
- package/scripts/report/report-lint-child-summary.ts +94 -0
- package/scripts/report/report-lint-cli.ts +26 -7
- package/scripts/report/report-lint.ts +2 -20
- package/scripts/review/prose-stale-cli.ts +88 -0
- package/scripts/review/prose-stale-scan.ts +152 -0
- package/scripts/review/review-attest.ts +73 -23
- package/scripts/review/review-brief-cli.ts +68 -19
- package/scripts/review/review-brief-paths.ts +22 -0
- package/scripts/review/review-brief.ts +14 -7
- package/scripts/review/review-comment-carry.ts +47 -0
- package/scripts/review/review-comment-round.ts +85 -22
- package/scripts/review/review-finding-ledger.ts +109 -7
- package/scripts/review/review-finding-text.ts +43 -0
- package/scripts/review/review-interaction.ts +11 -0
- package/scripts/review/review-lane-handover.ts +68 -0
- package/scripts/review/review-lint-cli.ts +57 -0
- package/scripts/review/review-lint-findings.ts +92 -0
- package/scripts/review/review-lint-sections.ts +80 -0
- package/scripts/review/review-lint.ts +221 -0
- package/scripts/review/review-prior-findings.ts +102 -0
- package/scripts/review/review-readers-scan.ts +4 -2
- package/scripts/review/review-record-cli.ts +177 -54
- package/scripts/review/review-record-code-round.ts +55 -0
- package/scripts/review/review-record-comment.ts +60 -0
- package/scripts/review/review-record-interaction.ts +35 -0
- package/scripts/review/review-record-pinned.ts +108 -0
- package/scripts/review/review-record.ts +25 -1
- package/scripts/review/review-reviewed-tree.ts +307 -0
- package/scripts/review/review-round-one-closure.ts +62 -0
- package/scripts/review/review-round-reuse.ts +106 -0
- package/scripts/review/review-round2-cli.ts +91 -29
- package/scripts/review/review-round2-skip-comment.ts +55 -0
- package/scripts/review/review-round2.ts +24 -8
- package/scripts/review/review-stamps.ts +7 -3
- package/scripts/review/review-stop-log.ts +20 -6
- package/scripts/review/review-stops-cli.ts +2 -1
- package/scripts/review/review-submit-cli.ts +101 -0
- package/scripts/rules/agent-model.ts +1 -1
- package/scripts/rules/bash-triggers.ts +3 -3
- package/scripts/rules/concise-reply.ts +88 -0
- package/scripts/rules/credential-body.ts +77 -0
- package/scripts/rules/decision-oracle.ts +1 -1
- package/scripts/rules/delivered-rules.ts +36 -17
- package/scripts/rules/destructive-command.ts +3 -11
- package/scripts/rules/direct-issue-comment.ts +34 -12
- package/scripts/rules/direct-issue-read.ts +191 -0
- package/scripts/rules/direct-pr-create.ts +4 -24
- package/scripts/rules/early-heartbeat.ts +2 -0
- package/scripts/rules/edit-tool.ts +27 -0
- package/scripts/rules/file-edit-rows.ts +17 -0
- package/scripts/rules/file-rewrite.ts +146 -0
- package/scripts/rules/firing-point.ts +3 -2
- package/scripts/rules/gh-api.ts +128 -7
- package/scripts/rules/gh-repo-selector.ts +84 -0
- package/scripts/rules/git-force.ts +2 -2
- package/scripts/rules/guard-remedy.ts +14 -5
- package/scripts/rules/implementation-cut.ts +61 -28
- package/scripts/rules/investigator-foreground.ts +231 -0
- package/scripts/rules/issue-citation.ts +15 -60
- package/scripts/rules/issue-comments.ts +9 -4
- package/scripts/rules/josh-git-bare.ts +1 -1
- package/scripts/rules/lane-background.ts +41 -13
- package/scripts/rules/lane-direct-tool.ts +107 -9
- package/scripts/rules/lane-interactive-ask.ts +4 -1
- package/scripts/rules/lane-park.ts +4 -2
- package/scripts/rules/lane-spilled-read.ts +90 -0
- package/scripts/rules/lane-split-park.ts +1 -1
- package/scripts/rules/lane-switch-main.ts +4 -4
- package/scripts/rules/last-prompt.ts +20 -47
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/permission-guards.ts +5 -1
- package/scripts/rules/piped-verification-shapes.ts +110 -0
- package/scripts/rules/piped-verification.ts +67 -17
- package/scripts/rules/poll-loop.ts +72 -9
- package/scripts/rules/pr-merge.ts +5 -5
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +88 -24
- package/scripts/rules/reply-language.ts +3 -17
- package/scripts/rules/reply-prose.ts +34 -0
- package/scripts/rules/repo-admin.ts +132 -0
- package/scripts/rules/reviewer-foreground.ts +88 -0
- package/scripts/rules/round-one-check-related.ts +120 -0
- package/scripts/rules/rule-body-guard.ts +5 -15
- package/scripts/rules/rule-value-cache.ts +53 -5
- package/scripts/rules/rule-value-candidates.ts +84 -0
- package/scripts/rules/rule-value-cli.ts +59 -11
- package/scripts/rules/rule-value.ts +17 -4
- package/scripts/rules/run-tail-rule.ts +1 -1
- package/scripts/rules/sed-in-place.ts +196 -0
- package/scripts/rules/shell-segments.ts +10 -2
- package/scripts/rules/stop-rule-rows.ts +1 -13
- package/scripts/rules/stop-rules.ts +33 -58
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +110 -23
- package/scripts/rules/timed-rerun.ts +1 -1
- package/scripts/rules/turn-filings.ts +90 -0
- package/scripts/rules/worktree-guard.ts +22 -7
- package/scripts/run/board/run-board-ending-reason.ts +6 -2
- package/scripts/run/board/run-board-fit.ts +24 -1
- package/scripts/run/board/run-board-found.ts +134 -0
- package/scripts/run/board/run-board-header.ts +8 -0
- package/scripts/run/board/run-board-labels.ts +11 -1
- package/scripts/run/board/run-board-lane-time.ts +1 -0
- package/scripts/run/board/run-board-link.ts +15 -2
- package/scripts/run/board/run-board-notes.ts +7 -1
- package/scripts/run/board/run-board-operation.ts +4 -2
- package/scripts/run/board/run-board-phase.ts +2 -5
- package/scripts/run/board/run-board-plan.ts +1 -1
- package/scripts/run/board/run-board-read.ts +46 -1
- package/scripts/run/board/run-board-reason.ts +20 -8
- package/scripts/run/board/run-board-render-checks.ts +3 -2
- package/scripts/run/board/run-board-render-drawn.ts +11 -0
- package/scripts/run/board/run-board-render-found.ts +157 -0
- package/scripts/run/board/run-board-render-lead.ts +9 -0
- package/scripts/run/board/run-board-render-legend.ts +19 -0
- package/scripts/run/board/run-board-render-tail.ts +1 -1
- package/scripts/run/board/run-board-render.ts +82 -18
- package/scripts/run/board/run-board-spin.ts +1 -3
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +39 -10
- package/scripts/run/carry/run-carry-ended.ts +3 -2
- package/scripts/run/carry/run-carry.ts +3 -2
- package/scripts/run/cut/run-cut-cli.ts +33 -133
- package/scripts/run/cut/run-cut-digest.ts +152 -0
- package/scripts/run/cut/run-cut-handoff.ts +52 -8
- package/scripts/run/cut/run-cut-report.ts +24 -3
- package/scripts/run/cut/run-cut-resume.ts +129 -0
- package/scripts/run/cut/run-cut-review.ts +105 -0
- package/scripts/run/cut/run-cut.ts +18 -7
- package/scripts/run/detached-launch.ts +1 -1
- package/scripts/run/entry/run-entry-cli.ts +88 -19
- package/scripts/run/entry/run-entry-spill.ts +83 -0
- package/scripts/run/entry/run-entry-stop.ts +5 -1
- package/scripts/run/entry/run-entry-trust.ts +91 -0
- package/scripts/run/event/run-event-baseline.ts +56 -0
- package/scripts/run/event/run-event-filed.ts +9 -3
- package/scripts/run/event/run-event-overrun.ts +197 -0
- package/scripts/run/event/run-event-render.ts +4 -0
- package/scripts/run/event/run-event-stream.ts +25 -3
- package/scripts/run/hold/run-stop-notify.ts +3 -8
- package/scripts/run/merge/run-merge-cap.ts +57 -0
- package/scripts/run/merge/run-merge-cli.ts +66 -23
- package/scripts/run/merge/run-merge-outcome.ts +2 -0
- package/scripts/run/merge/run-merge-ship.ts +13 -3
- package/scripts/run/merge/run-merge-signals.ts +40 -23
- package/scripts/run/merge/run-merge-steps.ts +56 -36
- package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
- package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
- package/scripts/run/merge/run-merge.ts +41 -14
- package/scripts/run/review/run-review-cli.ts +306 -20
- package/scripts/run/review/run-review-early-push.ts +121 -0
- package/scripts/run/review/run-review-join-read.ts +94 -0
- package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
- package/scripts/run/review/run-review-pinned-join.ts +216 -0
- package/scripts/run/review/run-review-pinned-steps.ts +383 -0
- package/scripts/run/review/run-review-pinned-tree.ts +37 -0
- package/scripts/run/review/run-review-pinned.ts +53 -0
- package/scripts/run/review/run-review-steps.ts +70 -34
- package/scripts/run/review/run-review.ts +26 -6
- package/scripts/run/run-parent-cut-hook.ts +3 -4
- package/scripts/run/run-prep.ts +1 -0
- package/scripts/run/run-stage-read.ts +2 -6
- package/scripts/run/run-step-cli.ts +17 -5
- package/scripts/run/run-step-lane-plan.ts +27 -0
- package/scripts/run/run-step-section.ts +26 -7
- package/scripts/run/run-step.ts +5 -2
- package/scripts/run/ship/run-ship-args.ts +19 -0
- package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
- package/scripts/run/ship/run-ship-cli.ts +131 -39
- package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
- package/scripts/run/ship/run-ship-detach.ts +10 -4
- package/scripts/run/ship/run-ship-handover.ts +106 -0
- package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
- package/scripts/run/ship/run-ship-lap.ts +63 -0
- package/scripts/run/ship/run-ship-next.ts +27 -26
- package/scripts/run/ship/run-ship-overlap.ts +8 -3
- package/scripts/run/ship/run-ship-place.ts +9 -2
- package/scripts/run/ship/run-ship-planned.ts +92 -0
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
- package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
- package/scripts/run/ship/run-ship-preflight.ts +16 -4
- package/scripts/run/ship/run-ship-probe.ts +23 -9
- package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
- package/scripts/run/ship/run-ship-relaunch.ts +51 -0
- package/scripts/run/ship/run-ship-return.ts +103 -29
- package/scripts/run/ship/run-ship-review-reason.ts +24 -0
- package/scripts/run/ship/run-ship-review-steps.ts +230 -59
- package/scripts/run/ship/run-ship-review.ts +171 -51
- package/scripts/run/ship/run-ship-reviewer.ts +138 -42
- package/scripts/run/ship/run-ship-scoped.ts +39 -10
- package/scripts/run/ship/run-ship-stage.ts +49 -13
- package/scripts/run/ship/run-ship-steps.ts +24 -4
- package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
- package/scripts/run/ship/run-ship-stop-context.ts +50 -13
- package/scripts/run/ship/run-ship-sync.ts +171 -30
- package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
- package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
- package/scripts/run/ship/run-ship-waiter.ts +114 -0
- package/scripts/run/ship/run-ship.ts +16 -2
- package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
- package/scripts/run/wake/run-wake-cli.ts +6 -113
- package/scripts/run/wake/run-wake-code.ts +53 -11
- package/scripts/run/wake/run-wake-driver.ts +5 -3
- package/scripts/run/wake/run-wake-finish.ts +137 -0
- package/scripts/run/wake/run-wake-loop.ts +5 -3
- package/scripts/run/wake/run-wake-ports.ts +11 -3
- package/scripts/run/wake/run-wake-session.ts +2 -0
- package/scripts/run/wake/run-wake.ts +3 -2
- package/scripts/safe-chain/ci-installer-pin.ts +49 -8
- package/scripts/safe-chain/project-config.ts +3 -2
- package/scripts/security/secretlint-scan.ts +42 -9
- package/scripts/security/security-audit-provision-main.ts +9 -1
- package/scripts/security/security-audit-provision.ts +1 -1
- package/scripts/security/security-audit.ts +21 -1
- package/scripts/security/sonar-file.ts +10 -5
- package/scripts/split/split-assess.ts +12 -3
- package/scripts/split/split-early-notice.ts +116 -0
- package/scripts/split/split-measure.ts +20 -2
- package/scripts/split/split-resolution.ts +107 -0
- package/scripts/test/direct-importers.ts +185 -0
- package/scripts/test/pure-files-entries.ts +83 -0
- package/scripts/test/related-heavy-tests.ts +81 -0
- package/scripts/test/test-no-base.ts +88 -0
- package/scripts/test/test-pinned-cache-logic.ts +363 -0
- package/scripts/test/test-pinned-cache.ts +337 -0
- package/scripts/test/test-pinned-emit.ts +49 -0
- package/scripts/test/test-pinned-functions.ts +116 -0
- package/scripts/test/test-pinned-ledger.ts +80 -0
- package/scripts/test/test-pinned-logic.ts +208 -20
- package/scripts/test/test-pinned-main.ts +84 -18
- package/scripts/test/test-pinned-pool.ts +73 -0
- package/scripts/test/test-pinned-probe.ts +254 -0
- package/scripts/test/test-pinned-prune-logic.ts +118 -0
- package/scripts/test/test-pinned-reach.ts +194 -0
- package/scripts/test/test-pinned-record.ts +196 -0
- package/scripts/test/test-pinned-rewire.ts +148 -0
- package/scripts/test/test-pinned-spawn.ts +81 -0
- package/scripts/test/test-pinned-stage.ts +70 -0
- package/scripts/test/test-pinned.ts +258 -47
- package/scripts/test/test-reach-cli.ts +61 -0
- package/scripts/test/test-read-map.ts +35 -11
- package/scripts/test/test-read-recording.ts +17 -5
- package/scripts/test/test-read-trace.ts +26 -8
- package/scripts/test/test-red-logic.ts +46 -7
- package/scripts/test/test-red.ts +34 -14
- package/scripts/test/test-related-claim.ts +37 -0
- package/scripts/test/test-related-main.ts +4 -1
- package/scripts/test/test-related-moved.ts +91 -0
- package/scripts/test/test-related-record.ts +43 -23
- package/scripts/test/test-related-scope.ts +99 -34
- package/scripts/test/test-related-synced.ts +135 -0
- package/scripts/test/test-related-touched.ts +84 -0
- package/scripts/test/test-related.ts +167 -83
- package/scripts/test/test-unit-guard.ts +28 -5
- package/scripts/test/unit-durations.ts +41 -0
- package/scripts/test/unit-worker-share.ts +5 -4
- package/scripts/test/vitest-cache-directory.ts +19 -0
- package/scripts/test/vitest-cli-excludes.ts +14 -0
- package/scripts/test/vitest-failure-summary.ts +111 -0
- package/scripts/test/whole-suite-files.ts +12 -1
- package/scripts/time-runtime/time-batch-guard.ts +42 -4
- package/scripts/time-runtime/time-bundle-call.ts +1 -1
- package/scripts/time-runtime/time-bundles.ts +36 -5
- package/scripts/time-runtime/time-markers.ts +2 -2
- package/scripts/time-runtime/time-search-call.ts +38 -0
- package/scripts/time-runtime/time-shell.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +17 -5
- package/scripts/version/development-engines-verdict.ts +124 -0
- package/scripts/version/latest-corepack.ts +2 -0
- package/templates/gitignore +2 -0
- package/templates/workflows/ci.yml +41 -0
- package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
- package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
- package/dist/hooks/chunk-TUIFS5E7.js +0 -72
- package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
- package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
- package/scripts/rules/filing-cap.ts +0 -139
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SnapshotObserver } from '#scripts/gh/git-pr-checks'
|
|
2
2
|
import type { PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
|
|
3
|
-
import {
|
|
3
|
+
import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
|
|
4
4
|
import type { CodeRabbitState } from '#scripts/run/event/run-event-checks'
|
|
5
5
|
import { followup_checks_event } from './followup-checks-event'
|
|
6
6
|
|
|
@@ -57,26 +57,29 @@ function observer_of(wait: CodeRabbitWait): SnapshotObserver {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
function
|
|
62
|
-
return async function
|
|
63
|
-
await
|
|
64
|
-
|
|
60
|
+
// Every observer on every poll, started in order — the board's event line and the wait's timers.
|
|
61
|
+
function all(...observers: ReadonlyArray<SnapshotObserver>): SnapshotObserver {
|
|
62
|
+
return async function observe_all(snapshot: PrStateSnapshot): Promise<void> {
|
|
63
|
+
await Promise.all(
|
|
64
|
+
observers.map(async (observer) => {
|
|
65
|
+
await observer(snapshot)
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
async function record_part(stage: string, elapsed_ms: number): Promise<void> {
|
|
69
|
-
if (elapsed_ms > NO_TIME) await
|
|
72
|
+
if (elapsed_ms > NO_TIME) await lane_ledger_write.record_stage({ stage, elapsed_ms })
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
// **A run that never saw CodeRabbit waiting records nothing**, so a row's run count is how many
|
|
73
76
|
// followups CodeRabbit held up, and its median is over those.
|
|
74
77
|
async function record(wait: CodeRabbitWait): Promise<void> {
|
|
75
|
-
await record_part(
|
|
76
|
-
await record_part(
|
|
78
|
+
await record_part(lane_ledger_write.CODERABBIT_WAIT_STAGE, wait.wait_ms)
|
|
79
|
+
await record_part(lane_ledger_write.CODERABBIT_RATE_LIMIT_STAGE, wait.rate_limited_ms)
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
const followup_coderabbit_wait = {
|
|
82
|
+
const followup_coderabbit_wait = { all, new_wait, observe, observer_of, record }
|
|
80
83
|
|
|
81
84
|
export type { CodeRabbitWait }
|
|
82
85
|
export { followup_coderabbit_wait }
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { git_gh_issue_write } from '#scripts/gh/git-gh-issue-write'
|
|
3
|
+
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
4
|
+
import {
|
|
5
|
+
observation_record,
|
|
6
|
+
REFUSED_VERDICT,
|
|
7
|
+
type ObservationEntry,
|
|
8
|
+
} from '#scripts/observations/observation-record'
|
|
9
|
+
import { review_comment_round } from '#scripts/review/review-comment-round'
|
|
10
|
+
import { vitest_failure_summary } from '#scripts/test/vitest-failure-summary'
|
|
11
|
+
|
|
12
|
+
// A CI job that ended `failure` because a unit test went red can still owe nothing to the change: a test
|
|
13
|
+
// the diff never touched fails under runner load (joshuafolkken/kit#5152, a frame-budget test on a busy
|
|
14
|
+
// runner). `followup` reruns such a job once, as it does a stalled one (`followup-job-rerun.ts`), and
|
|
15
|
+
// these are the readings that decide it: the failed tests a job's log names, and whether any of them
|
|
16
|
+
// belongs to the change.
|
|
17
|
+
//
|
|
18
|
+
// **A test belongs to the change when its own file or its subject module moved** — `foo.claim.test.ts`
|
|
19
|
+
// tests `foo.claim.ts` and `foo.ts`. A test reaching a moved module only through an import is missed,
|
|
20
|
+
// and costs one rerun: the second red still stops `followup`, since a rerun attempt is never rerun.
|
|
21
|
+
|
|
22
|
+
// GitHub Actions stamps every log line `<ISO timestamp> `, which vitest's ` FAIL ` line does not begin with.
|
|
23
|
+
const LOG_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\S+Z /u
|
|
24
|
+
const TEST_SUFFIX = /\.test(?<extension>\.[^./]+)$/u
|
|
25
|
+
const SEGMENT_SEPARATOR = '.'
|
|
26
|
+
const OBSERVATION_SLUG = 'followup-unrelated-test-rerun'
|
|
27
|
+
const OBSERVATION_DEPTH = 'd1'
|
|
28
|
+
const NAME_SEPARATOR = '; '
|
|
29
|
+
const UNNAMED_ISSUE =
|
|
30
|
+
'Did not record the failed tests outside the change: the branch names no issue to post them on.'
|
|
31
|
+
|
|
32
|
+
const RERUN_VERDICT = 'rerun'
|
|
33
|
+
const KEPT_VERDICT = 'kept'
|
|
34
|
+
const UNNAMED_VERDICT = 'unnamed'
|
|
35
|
+
|
|
36
|
+
type TestVerdict = typeof RERUN_VERDICT | typeof KEPT_VERDICT | typeof UNNAMED_VERDICT
|
|
37
|
+
|
|
38
|
+
interface FailedTests {
|
|
39
|
+
files: ReadonlyArray<string>
|
|
40
|
+
names: ReadonlyArray<string>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface UnrelatedFailure {
|
|
44
|
+
job: string
|
|
45
|
+
tests: FailedTests
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// The failed tests vitest named in a job's log — empty when the job failed on anything but a test.
|
|
49
|
+
function read_failed_tests(log: string): FailedTests {
|
|
50
|
+
const lines = log.split('\n').map((line) => line.replace(LOG_TIMESTAMP, ''))
|
|
51
|
+
const names = vitest_failure_summary.failed_tests(lines.join('\n'))
|
|
52
|
+
|
|
53
|
+
return { files: vitest_failure_summary.failed_files(names), names }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The test file itself, then the module each leading run of its name's segments names.
|
|
57
|
+
function subjects_of(test_file: string): ReadonlyArray<string> {
|
|
58
|
+
const extension = TEST_SUFFIX.exec(test_file)?.groups?.['extension']
|
|
59
|
+
|
|
60
|
+
if (extension === undefined) return [test_file]
|
|
61
|
+
|
|
62
|
+
const directory = path.posix.dirname(test_file)
|
|
63
|
+
const segments = path.posix.basename(test_file, `.test${extension}`).split(SEGMENT_SEPARATOR)
|
|
64
|
+
const modules = segments.map((_, index) =>
|
|
65
|
+
path.posix.join(
|
|
66
|
+
directory,
|
|
67
|
+
`${segments.slice(0, index + 1).join(SEGMENT_SEPARATOR)}${extension}`,
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return [test_file, ...modules]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function is_in_change(test_file: string, changed: ReadonlySet<string>): boolean {
|
|
75
|
+
return subjects_of(test_file).some((subject) => changed.has(subject))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// **`rerun` only when the log named a failed test and none of them belongs to the change**, `kept`
|
|
79
|
+
// when one does. A job red with no test named — a lint, a build, a job gathering others' results — is
|
|
80
|
+
// `unnamed`: rerun only as one waiting on a job red on tests outside the change (`followup-job-needs.ts`).
|
|
81
|
+
function verdict_of(tests: FailedTests, changed: ReadonlySet<string>): TestVerdict {
|
|
82
|
+
if (tests.files.length === 0) return UNNAMED_VERDICT
|
|
83
|
+
|
|
84
|
+
return tests.files.every((file) => !is_in_change(file, changed)) ? RERUN_VERDICT : KEPT_VERDICT
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The ledger line a rerun leaves, so a test that keeps failing beside unrelated changes is counted
|
|
88
|
+
// rather than retried into silence. A ledger line's fields are split on ` | `, so the `|<project>|`
|
|
89
|
+
// prefix a workspace test name carries is rewritten.
|
|
90
|
+
function observation_of(failures: ReadonlyArray<UnrelatedFailure>): ObservationEntry {
|
|
91
|
+
const files = new Set(failures.flatMap(({ tests }) => tests.files))
|
|
92
|
+
const names = new Set(failures.flatMap(({ tests }) => tests.names))
|
|
93
|
+
const listed = [...names].map((name) => name.replaceAll('|', '/'))
|
|
94
|
+
const jobs = failures.map(({ job }) => job)
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
slug: OBSERVATION_SLUG,
|
|
98
|
+
depth: OBSERVATION_DEPTH,
|
|
99
|
+
where: [...files].join(', '),
|
|
100
|
+
what: `followup reran ${jobs.join(', ')}; failed outside the change: ${listed.join(NAME_SEPARATOR)}`,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// **The line is posted on the issue, never appended to the tree** (joshuafolkken/kit#5197): the rerun
|
|
105
|
+
// happens during the CI wait, after `prepare_merge` committed the ledger, so an appended line would
|
|
106
|
+
// either be lost with the lane or need a push of its own that restarts CI. `review-comment-round.ts`
|
|
107
|
+
// carries it into the ledger once the issue closes; the count is still read off this tree's ledger.
|
|
108
|
+
async function post(entry: ObservationEntry, issue: number): Promise<string> {
|
|
109
|
+
const assessment = await observation_record.assess({
|
|
110
|
+
entry,
|
|
111
|
+
checkout: observation_ledger_home.ledger_root(),
|
|
112
|
+
now: new Date(),
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
if (assessment.verdict === REFUSED_VERDICT) return `${REFUSED_VERDICT}: ${assessment.reason}`
|
|
116
|
+
|
|
117
|
+
await git_gh_issue_write.issue_comment(
|
|
118
|
+
String(issue),
|
|
119
|
+
review_comment_round.observation_body_of([assessment.line]),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return assessment.verdict
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// A post that fails is reported and does not stop the step it describes; a branch naming no issue has
|
|
126
|
+
// nowhere to post.
|
|
127
|
+
async function record_entry(entry: ObservationEntry, issue: number | undefined): Promise<void> {
|
|
128
|
+
if (issue === undefined) {
|
|
129
|
+
console.info(`⚠️ ${UNNAMED_ISSUE}`)
|
|
130
|
+
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
console.info(`📝 Recorded the ${entry.slug} observation: ${await post(entry, issue)}`)
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.info(`⚠️ Could not record the ${entry.slug} observation: ${String(error)}`)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function record(
|
|
142
|
+
failures: ReadonlyArray<UnrelatedFailure>,
|
|
143
|
+
issue: number | undefined,
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
if (failures.length === 0) return
|
|
146
|
+
|
|
147
|
+
await record_entry(observation_of(failures), issue)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const followup_failed_tests = {
|
|
151
|
+
observation_of,
|
|
152
|
+
read_failed_tests,
|
|
153
|
+
record,
|
|
154
|
+
record_entry,
|
|
155
|
+
subjects_of,
|
|
156
|
+
verdict_of,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type { FailedTests, TestVerdict, UnrelatedFailure }
|
|
160
|
+
export { followup_failed_tests, KEPT_VERDICT, OBSERVATION_SLUG, RERUN_VERDICT, UNNAMED_VERDICT }
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import { yaml_document } from '#scripts/lib/yaml-document'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
// A job that only gathers others' results — kit's `Checks`, which `needs` the unit shards — ends
|
|
6
|
+
// `failure` beside a shard red on a test the change never touched, and its log names no test
|
|
7
|
+
// (joshuafolkken/kit#5171). It fails because of that shard alone, so it is rerun with it; the rerun
|
|
8
|
+
// endpoint reruns a failed job's dependents in any case. **The workflow's own `needs` says which job
|
|
9
|
+
// waits on which**, read from the checkout the pull request's head is.
|
|
10
|
+
//
|
|
11
|
+
// A job is known by the name a check run carries, which the workflow writes as a template: an
|
|
12
|
+
// expression in it matches anything, and a name with none gains the ` (<matrix values>)` suffix GitHub
|
|
13
|
+
// appends to a matrix job. A job no pattern matches — a reusable workflow's, a name built otherwise —
|
|
14
|
+
// is waited on by nothing here, so its failure is kept.
|
|
15
|
+
|
|
16
|
+
const EXPRESSION = /\$\{\{[^}]*\}\}/u
|
|
17
|
+
const EXPRESSION_MARK = '${{'
|
|
18
|
+
const ANY_TEXT = '.+'
|
|
19
|
+
const MATRIX_SUFFIX = String.raw`(?: \(.+\))?`
|
|
20
|
+
const REGEX_SPECIAL = /[$()*+.?[\\\]^{|}]/gu
|
|
21
|
+
|
|
22
|
+
const needs_schema = z.array(z.string())
|
|
23
|
+
|
|
24
|
+
const job_schema = z.looseObject({
|
|
25
|
+
name: z.string().optional(),
|
|
26
|
+
needs: z.union([z.string(), needs_schema]).optional(),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const workflow_schema = z.looseObject({ jobs: z.record(z.string(), job_schema) })
|
|
30
|
+
|
|
31
|
+
type WorkflowJobs = z.infer<typeof workflow_schema>['jobs']
|
|
32
|
+
|
|
33
|
+
interface JobNeeds {
|
|
34
|
+
pattern: RegExp
|
|
35
|
+
needs: ReadonlyArray<RegExp>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type NeedsGraph = ReadonlyArray<JobNeeds>
|
|
39
|
+
|
|
40
|
+
function escape(text: string): string {
|
|
41
|
+
return text.replaceAll(REGEX_SPECIAL, String.raw`\$&`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function name_pattern(id: string, name: string = id): RegExp {
|
|
45
|
+
const source = name.includes(EXPRESSION_MARK)
|
|
46
|
+
? name
|
|
47
|
+
.split(EXPRESSION)
|
|
48
|
+
.map((part) => escape(part))
|
|
49
|
+
.join(ANY_TEXT)
|
|
50
|
+
: `${escape(name)}${MATRIX_SUFFIX}`
|
|
51
|
+
|
|
52
|
+
return new RegExp(`^${source}$`, 'u')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function needs_list(needs: string | ReadonlyArray<string> = []): ReadonlyArray<string> {
|
|
56
|
+
return typeof needs === 'string' ? [needs] : needs
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function pattern_of(jobs: WorkflowJobs, id: string): RegExp {
|
|
60
|
+
return name_pattern(id, jobs[id]?.name)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The jobs of a workflow, each with the names of the jobs it needs; a workflow of another shape has
|
|
64
|
+
// none. YAML that does not parse throws, which `read_graph` answers as none.
|
|
65
|
+
function parse_graph(workflow: string): NeedsGraph {
|
|
66
|
+
const parsed = workflow_schema.safeParse(yaml_document.parse_yaml(workflow))
|
|
67
|
+
|
|
68
|
+
if (!parsed.success) return []
|
|
69
|
+
|
|
70
|
+
const { jobs } = parsed.data
|
|
71
|
+
|
|
72
|
+
return Object.entries(jobs).map(([id, job]) => ({
|
|
73
|
+
pattern: pattern_of(jobs, id),
|
|
74
|
+
needs: needs_list(job.needs).map((need) => pattern_of(jobs, need)),
|
|
75
|
+
}))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// **Whether the job named `job_name` needs one of the jobs named in `upstream`.**
|
|
79
|
+
function is_waiting_on(
|
|
80
|
+
graph: NeedsGraph,
|
|
81
|
+
job_name: string,
|
|
82
|
+
upstream: ReadonlyArray<string>,
|
|
83
|
+
): boolean {
|
|
84
|
+
const needs = graph.filter(({ pattern }) => pattern.test(job_name)).flatMap((job) => job.needs)
|
|
85
|
+
|
|
86
|
+
return upstream.some((name) => needs.some((pattern) => pattern.test(name)))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function read_graph(workflow_path: string): Promise<NeedsGraph> {
|
|
90
|
+
try {
|
|
91
|
+
return parse_graph(await readFile(workflow_path, 'utf8'))
|
|
92
|
+
} catch {
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const followup_job_needs = { is_waiting_on, parse_graph, read_graph }
|
|
98
|
+
|
|
99
|
+
export type { NeedsGraph }
|
|
100
|
+
export { followup_job_needs }
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
|
|
2
|
+
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
3
|
+
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
4
|
+
import { git_gh_helpers } from '#scripts/gh/git-gh-helpers'
|
|
5
|
+
import { changed_paths } from '#scripts/git/changed-paths'
|
|
6
|
+
import { git_branch } from '#scripts/git/git-branch'
|
|
7
|
+
import { poll } from '#scripts/lib/poll'
|
|
8
|
+
import { z } from 'zod'
|
|
9
|
+
import {
|
|
10
|
+
followup_failed_tests,
|
|
11
|
+
KEPT_VERDICT,
|
|
12
|
+
RERUN_VERDICT,
|
|
13
|
+
UNNAMED_VERDICT,
|
|
14
|
+
type FailedTests,
|
|
15
|
+
type TestVerdict,
|
|
16
|
+
} from './followup-failed-tests'
|
|
17
|
+
import { followup_job_needs, type NeedsGraph } from './followup-job-needs'
|
|
18
|
+
import { followup_main_move } from './followup-main-move'
|
|
19
|
+
|
|
20
|
+
// A GitHub Actions job whose every step succeeded can still end `cancelled` or `timed_out`: the runner
|
|
21
|
+
// stops reporting after the last step and the job's `timeout-minutes` cuts it off (joshuafolkken/kit#4813,
|
|
22
|
+
// two 3-second jobs cut at 2m0s). Nothing in the change caused it, so `followup` reruns such a job once
|
|
23
|
+
// — as `git-gh-retry.ts` retries a GitHub 5xx — rather than failing a pull request that is green.
|
|
24
|
+
//
|
|
25
|
+
// **A job red only on tests outside the change is rerun the same way** (joshuafolkken/kit#5171): a test
|
|
26
|
+
// the diff never touched failing under runner load stopped `followup` and waited for a relaunched
|
|
27
|
+
// session to type the rerun by hand. Only the run's first attempt qualifies, so a test that fails again
|
|
28
|
+
// stops `followup` as before, and each such rerun leaves an observation (`followup-failed-tests.ts`).
|
|
29
|
+
// A job that failed only for waiting on such a job is rerun with it (`followup-job-needs.ts`).
|
|
30
|
+
//
|
|
31
|
+
// The rerun endpoint reruns **every** failed job of a run, and GitHub refuses it while the run is still
|
|
32
|
+
// going. So each run is waited out first, and it is rerun only when every job that failed in it
|
|
33
|
+
// qualifies: a run holding a job that failed on the change itself is never rerun, or a test the change
|
|
34
|
+
// broke could be retried into a merge.
|
|
35
|
+
|
|
36
|
+
const FAILURE_CONCLUSION = 'failure'
|
|
37
|
+
const FAILED_CONCLUSIONS = [FAILURE_CONCLUSION, 'cancelled', 'timed_out'] as const
|
|
38
|
+
const FAILED_JOB_CONCLUSIONS: ReadonlySet<string> = new Set(FAILED_CONCLUSIONS)
|
|
39
|
+
const ACTIONS_APP_SLUG = 'github-actions'
|
|
40
|
+
const PASSING_STEP_CONCLUSIONS: ReadonlySet<string> = new Set(['success', 'skipped'])
|
|
41
|
+
const RUN_COMPLETED = 'completed'
|
|
42
|
+
const FIRST_ATTEMPT = 1
|
|
43
|
+
|
|
44
|
+
// The longest `timeout-minutes` in `ci.yml` is 10; a run still going past that is not one a rerun helps.
|
|
45
|
+
const COMPLETE_ATTEMPTS = 40
|
|
46
|
+
const COMPLETE_INTERVAL_MS = 15_000
|
|
47
|
+
|
|
48
|
+
// The rerun answers once the new attempt exists, but its check runs can trail it; a re-judge that read
|
|
49
|
+
// the failed run again would fail on the very job just rerun.
|
|
50
|
+
const REGISTER_ATTEMPTS = 10
|
|
51
|
+
const REGISTER_INTERVAL_MS = 3000
|
|
52
|
+
|
|
53
|
+
const failed_check_schema = z.looseObject({
|
|
54
|
+
id: z.number(),
|
|
55
|
+
conclusion: z.enum(FAILED_CONCLUSIONS),
|
|
56
|
+
app: z.looseObject({ slug: z.literal(ACTIONS_APP_SLUG) }),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const rollup_schema = z.looseObject({ statusCheckRollup: z.array(z.unknown()) })
|
|
60
|
+
|
|
61
|
+
// REST answers `null` for a step that never finished; a test fixture may leave the field out.
|
|
62
|
+
const step_schema = z.looseObject({ conclusion: z.string().nullish() })
|
|
63
|
+
|
|
64
|
+
const job_schema = z.looseObject({
|
|
65
|
+
id: z.number(),
|
|
66
|
+
name: z.string(),
|
|
67
|
+
run_id: z.number(),
|
|
68
|
+
run_attempt: z.number().nullish(),
|
|
69
|
+
conclusion: z.string().nullish(),
|
|
70
|
+
steps: z.array(step_schema),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// `path` is the workflow file's, relative to the repository root.
|
|
74
|
+
const run_schema = z.looseObject({ status: z.string(), path: z.string().optional() })
|
|
75
|
+
|
|
76
|
+
const jobs_page_schema = z.looseObject({ jobs: z.array(job_schema) })
|
|
77
|
+
|
|
78
|
+
type Job = z.infer<typeof job_schema>
|
|
79
|
+
|
|
80
|
+
// A failed job and what its reading says, with the failed tests when its log named some.
|
|
81
|
+
interface Judged {
|
|
82
|
+
job: Job
|
|
83
|
+
verdict: TestVerdict
|
|
84
|
+
tests?: FailedTests
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function actions_api_path(rest: string): string {
|
|
88
|
+
return `${git_gh_api_path.repo_api_path()}/actions/${rest}`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function read_actions<T>(rest: string, schema: z.ZodType<T>): Promise<T> {
|
|
92
|
+
const json = await git_gh_exec.exec_gh_api({ path: actions_api_path(rest) })
|
|
93
|
+
|
|
94
|
+
return schema.parse(JSON.parse(json))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function read_failed_check_ids(branch_name: string): Promise<Array<number>> {
|
|
98
|
+
const { snapshot_json } = await git_gh_command.pr_get_checks_snapshot(branch_name)
|
|
99
|
+
const rollup = rollup_schema.parse(JSON.parse(snapshot_json)).statusCheckRollup
|
|
100
|
+
|
|
101
|
+
return rollup.flatMap((item) => {
|
|
102
|
+
const parsed = failed_check_schema.safeParse(item)
|
|
103
|
+
|
|
104
|
+
return parsed.success ? parsed.data.id : []
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// A check run of GitHub Actions has its job's id, which is how the job — and so its run — is found.
|
|
109
|
+
async function read_failed_run_ids(branch_name: string): Promise<Array<number>> {
|
|
110
|
+
const ids = await read_failed_check_ids(branch_name)
|
|
111
|
+
const jobs = await Promise.all(
|
|
112
|
+
ids.map(async (id) => await read_actions(`jobs/${String(id)}`, job_schema)),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return [...new Set(jobs.map((job) => job.run_id))]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function is_run_completed(run_id: number): Promise<boolean> {
|
|
119
|
+
const run = await read_actions(`runs/${String(run_id)}`, run_schema)
|
|
120
|
+
|
|
121
|
+
return run.status === RUN_COMPLETED
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function await_run_completed(run_id: number): Promise<boolean> {
|
|
125
|
+
return await poll.poll_until(async () => await is_run_completed(run_id), {
|
|
126
|
+
attempts: COMPLETE_ATTEMPTS,
|
|
127
|
+
interval_ms: COMPLETE_INTERVAL_MS,
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function has_every_step_passed(job: Job): boolean {
|
|
132
|
+
if (job.steps.length === 0) return false
|
|
133
|
+
|
|
134
|
+
return job.steps.every((step) => PASSING_STEP_CONCLUSIONS.has(step.conclusion ?? ''))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function is_first_failure(job: Job): boolean {
|
|
138
|
+
return (
|
|
139
|
+
job.conclusion === FAILURE_CONCLUSION && (job.run_attempt ?? FIRST_ATTEMPT) <= FIRST_ATTEMPT
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// A job whose every step passed stopped on the runner; one red on its first attempt is judged by the
|
|
144
|
+
// failed tests its log names. The log endpoint redirects to the plain text, which `gh api` follows; a
|
|
145
|
+
// vitest log is colored.
|
|
146
|
+
async function judge_job(job: Job, changed: ReadonlySet<string>): Promise<Judged> {
|
|
147
|
+
if (has_every_step_passed(job)) return { job, verdict: RERUN_VERDICT }
|
|
148
|
+
if (!is_first_failure(job)) return { job, verdict: KEPT_VERDICT }
|
|
149
|
+
|
|
150
|
+
const log = await git_gh_exec.exec_gh_api({
|
|
151
|
+
path: actions_api_path(`jobs/${String(job.id)}/logs`),
|
|
152
|
+
should_allow_escapes: true,
|
|
153
|
+
})
|
|
154
|
+
const tests = followup_failed_tests.read_failed_tests(log)
|
|
155
|
+
|
|
156
|
+
return { job, verdict: followup_failed_tests.verdict_of(tests, changed), tests }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function is_red_on_tests({ verdict, tests }: Judged): boolean {
|
|
160
|
+
return verdict === RERUN_VERDICT && tests !== undefined
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// A job red with no test named qualifies only as one waiting on a job red on tests outside the change.
|
|
164
|
+
function settle_unnamed(judged: ReadonlyArray<Judged>, graph: NeedsGraph): Array<Judged> {
|
|
165
|
+
const red_on_tests = judged.filter((entry) => is_red_on_tests(entry)).map(({ job }) => job.name)
|
|
166
|
+
|
|
167
|
+
return judged.map((entry) => {
|
|
168
|
+
if (entry.verdict !== UNNAMED_VERDICT) return entry
|
|
169
|
+
|
|
170
|
+
const is_waiting = followup_job_needs.is_waiting_on(graph, entry.job.name, red_on_tests)
|
|
171
|
+
|
|
172
|
+
return is_waiting ? { job: entry.job, verdict: RERUN_VERDICT } : entry
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// The workflow is read only when a job named no test; a run without its `path` waits on nothing.
|
|
177
|
+
async function read_needs_graph(
|
|
178
|
+
run_id: number,
|
|
179
|
+
judged: ReadonlyArray<Judged>,
|
|
180
|
+
): Promise<NeedsGraph> {
|
|
181
|
+
if (judged.every(({ verdict }) => verdict !== UNNAMED_VERDICT)) return []
|
|
182
|
+
|
|
183
|
+
const run = await read_actions(`runs/${String(run_id)}`, run_schema)
|
|
184
|
+
|
|
185
|
+
return run.path === undefined ? [] : await followup_job_needs.read_graph(run.path)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// **The jobs the rerun would retry**, or `undefined` when the run must not be rerun: still going, or
|
|
189
|
+
// holding a failed job that does not qualify.
|
|
190
|
+
async function read_rerun_jobs(
|
|
191
|
+
run_id: number,
|
|
192
|
+
changed: ReadonlySet<string>,
|
|
193
|
+
): Promise<Array<Judged> | undefined> {
|
|
194
|
+
if (!(await await_run_completed(run_id))) return undefined
|
|
195
|
+
const { jobs } = await read_actions(
|
|
196
|
+
`runs/${String(run_id)}/jobs?filter=latest&per_page=100`,
|
|
197
|
+
jobs_page_schema,
|
|
198
|
+
)
|
|
199
|
+
const failed = jobs.filter((job) => FAILED_JOB_CONCLUSIONS.has(job.conclusion ?? ''))
|
|
200
|
+
const judged = await Promise.all(failed.map(async (job) => await judge_job(job, changed)))
|
|
201
|
+
const settled = settle_unnamed(judged, await read_needs_graph(run_id, judged))
|
|
202
|
+
|
|
203
|
+
return settled.every(({ verdict }) => verdict === RERUN_VERDICT) ? settled : undefined
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// One request per run, not retried: a rerun that landed before a transient failure would be refused
|
|
207
|
+
// the second time.
|
|
208
|
+
async function rerun_runs(run_ids: ReadonlyArray<number>): Promise<void> {
|
|
209
|
+
await Promise.all(
|
|
210
|
+
run_ids.map(
|
|
211
|
+
async (run_id) =>
|
|
212
|
+
await git_gh_exec.exec_gh_api({
|
|
213
|
+
path: actions_api_path(`runs/${String(run_id)}/rerun-failed-jobs`),
|
|
214
|
+
method: 'POST',
|
|
215
|
+
should_retry: false,
|
|
216
|
+
}),
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function await_rerun_registered(
|
|
222
|
+
branch_name: string,
|
|
223
|
+
jobs: ReadonlyArray<Job>,
|
|
224
|
+
): Promise<void> {
|
|
225
|
+
const rerun_ids = new Set(jobs.map((job) => job.id))
|
|
226
|
+
|
|
227
|
+
await poll.poll_until(
|
|
228
|
+
async () => {
|
|
229
|
+
const failed = await read_failed_check_ids(branch_name)
|
|
230
|
+
|
|
231
|
+
return failed.every((id) => !rerun_ids.has(id))
|
|
232
|
+
},
|
|
233
|
+
{ attempts: REGISTER_ATTEMPTS, interval_ms: REGISTER_INTERVAL_MS },
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Every run must qualify: rerunning some would still leave the pull request red on the others. The
|
|
238
|
+
// change is read once, and only when a run failed.
|
|
239
|
+
async function find_rerun_jobs(branch_name: string): Promise<Array<Judged>> {
|
|
240
|
+
const run_ids = await read_failed_run_ids(branch_name)
|
|
241
|
+
|
|
242
|
+
if (run_ids.length === 0) return []
|
|
243
|
+
|
|
244
|
+
const changed = new Set(await changed_paths.read_changed_paths(false))
|
|
245
|
+
const per_run = await Promise.all(
|
|
246
|
+
run_ids.map(async (run_id) => await read_rerun_jobs(run_id, changed)),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return per_run.every((jobs) => jobs !== undefined) ? per_run.flat() : []
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function rerun(branch_name: string, entries: ReadonlyArray<Judged>): Promise<Array<string>> {
|
|
253
|
+
const jobs = entries.map(({ job }) => job)
|
|
254
|
+
const names = jobs.map((job) => job.name)
|
|
255
|
+
|
|
256
|
+
console.info(
|
|
257
|
+
`🔁 Rerunning CI jobs that stopped after every step passed or failed only outside the change: ${names.join(', ')}`,
|
|
258
|
+
)
|
|
259
|
+
await followup_failed_tests.record(
|
|
260
|
+
entries.flatMap(({ job, tests }) => (tests === undefined ? [] : { job: job.name, tests })),
|
|
261
|
+
git_branch.issue_from_branch(branch_name),
|
|
262
|
+
)
|
|
263
|
+
await rerun_runs([...new Set(jobs.map((job) => job.run_id))])
|
|
264
|
+
await await_rerun_registered(branch_name, jobs)
|
|
265
|
+
|
|
266
|
+
return names
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// A failed test main has since changed is merged before any rerun (`followup-main-move.ts`).
|
|
270
|
+
async function should_merge_first(
|
|
271
|
+
branch_name: string,
|
|
272
|
+
entries: ReadonlyArray<Judged>,
|
|
273
|
+
): Promise<boolean> {
|
|
274
|
+
const files = [...new Set(entries.flatMap(({ tests }) => tests?.files ?? []))]
|
|
275
|
+
|
|
276
|
+
return await followup_main_move.should_merge_first(
|
|
277
|
+
files,
|
|
278
|
+
git_branch.issue_from_branch(branch_name),
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// **Returns the names of the jobs it reran**, empty when there was none to rerun or main has to be
|
|
283
|
+
// merged first. A read or a rerun that fails is reported and answers empty: the rerun is a recovery,
|
|
284
|
+
// so the original failure stands.
|
|
285
|
+
async function rerun_failed_jobs(branch_name: string): Promise<Array<string>> {
|
|
286
|
+
try {
|
|
287
|
+
const entries = await find_rerun_jobs(branch_name)
|
|
288
|
+
|
|
289
|
+
if (entries.length === 0 || (await should_merge_first(branch_name, entries))) return []
|
|
290
|
+
|
|
291
|
+
return await rerun(branch_name, entries)
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.info(`⚠️ CI job rerun failed: ${git_gh_helpers.get_error_message_with_stderr(error)}`)
|
|
294
|
+
|
|
295
|
+
return []
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const followup_job_rerun = {
|
|
300
|
+
rerun_failed_jobs,
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export { followup_job_rerun, COMPLETE_ATTEMPTS, REGISTER_ATTEMPTS }
|