@joshuafolkken/kit 1.1178.0 → 1.1240.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/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +7 -3
- package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
- package/.claude/skills/workflow-commands/chain-rule.md +1 -1
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +10 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -1
- package/.claude/skills/workflow-commands/latest-gate.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
- package/.claude/skills/workflow-commands/prerequisite.md +2 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
- package/.codex/hooks.json +2 -2
- package/.cursorrules +6 -41
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/auto-tag.yml +3 -4
- package/.github/workflows/ci.yml +32 -31
- package/.github/workflows/dependabot-auto-merge.yml +13 -6
- package/.github/workflows/publish.yml +50 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +44 -38
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
- package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
- package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
- package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
- package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
- package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
- package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
- package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +203 -179
- package/dist/hooks/investigation-guard.js +6 -5
- package/dist/hooks/pretool-guard.js +12 -10
- package/dist/hooks/session-lang.js +2 -4
- package/dist/hooks/stop-guard.js +66 -72
- package/dist/josh.js +1088 -78
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-template.md +1 -0
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +16 -5
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-scope.ts +1 -1
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +3 -1
- package/scripts/build/build-commands.ts +66 -0
- package/scripts/build/build-hooks.ts +2 -1
- package/scripts/build/build-library.ts +12 -3
- package/scripts/build/build.ts +70 -0
- package/scripts/build/local-bin.ts +3 -13
- package/scripts/delegation/investigation-reads.ts +3 -1
- package/scripts/discovery/repo-origin.ts +5 -5
- package/scripts/doctor/doctor-consumer.ts +2 -2
- package/scripts/doctor/doctor-io.ts +2 -3
- package/scripts/document/capped-print-part.ts +30 -0
- package/scripts/document/capped-print.ts +3 -4
- package/scripts/document/document-byte-budget.ts +11 -2
- package/scripts/document/document-scan.ts +117 -0
- package/scripts/document/document-section.ts +143 -16
- package/scripts/document/entry-read-budget.ts +5 -3
- package/scripts/document/entry-read-set.ts +10 -89
- package/scripts/document/read-set-cost.ts +98 -0
- package/scripts/epic/epic-add-body.ts +1 -0
- package/scripts/epic/epic-audit-checks.ts +1 -1
- package/scripts/epic/epic-audit-rationale.ts +4 -3
- package/scripts/epic/epic-bundle-cli.ts +14 -3
- package/scripts/epic/epic-bundle-poll.ts +41 -0
- package/scripts/epic/epic-busy.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +3 -2
- package/scripts/epic/epic-classify.ts +1 -1
- package/scripts/epic/epic-cli-argv.ts +78 -46
- package/scripts/epic/epic-cli-remove.ts +11 -19
- package/scripts/epic/epic-cli.ts +74 -73
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +5 -5
- package/scripts/epic/epic-graph.ts +1 -1
- package/scripts/epic/epic-index.ts +1 -1
- package/scripts/epic/epic-issue.ts +2 -6
- package/scripts/epic/epic-lane-offer.ts +2 -2
- package/scripts/epic/epic-next.ts +29 -42
- package/scripts/epic/epic-solo-stale.ts +1 -1
- package/scripts/epic/epic-triage.ts +8 -4
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/followup/git-followup-finish.ts +2 -2
- package/scripts/followup/git-followup-label.ts +2 -2
- package/scripts/followup/git-followup-merged.ts +2 -2
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +4 -129
- package/scripts/gate/core-admission.ts +162 -0
- package/scripts/gate/core-budget.ts +190 -68
- package/scripts/gate/gate-plan.ts +80 -7
- package/scripts/gate/gate-report.ts +28 -9
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/machine-capacity.ts +175 -0
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +34 -10
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-pr-read.ts +64 -19
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr.ts +5 -1
- package/scripts/gh/git-pr-ai-review.ts +1 -1
- package/scripts/gh/git-pr-checks-eval.ts +1 -3
- package/scripts/gh/git-pr-checks-parse.ts +1 -1
- package/scripts/gh/git-pr-checks-watch.ts +6 -5
- package/scripts/gh/git-pr-checks.ts +7 -5
- package/scripts/gh/git-pr-coderabbit.ts +1 -1
- package/scripts/gh/git-pr-error.ts +1 -3
- package/scripts/gh/git-pr.ts +9 -13
- package/scripts/gh/github-issue-url.ts +2 -2
- package/scripts/git/constants.ts +2 -6
- package/scripts/git/git-command.ts +20 -218
- package/scripts/git/git-diff-reads.ts +225 -0
- package/scripts/git/git-error.ts +4 -12
- package/scripts/git/git-pre-push-hook.ts +139 -0
- package/scripts/git/git-prompt.ts +2 -6
- package/scripts/git/git-spawn-sync.ts +3 -1
- package/scripts/git/git-spawn.ts +1 -1
- package/scripts/git/git-staging.ts +4 -6
- package/scripts/git/git-status.ts +3 -9
- package/scripts/hooks/codex-hook-adapter.ts +1 -1
- package/scripts/hooks/edited-cspell.ts +2 -4
- package/scripts/hooks/format-edited-file.ts +3 -12
- package/scripts/hooks/pre-push-unit.ts +31 -9
- package/scripts/hooks/reserved-run.ts +2 -2
- package/scripts/hooks/run-hook.sh +5 -4
- package/scripts/hooks/stop-guard.ts +5 -3
- package/scripts/init/init-actions.ts +0 -5
- package/scripts/init/init-ai-copy.ts +13 -2
- package/scripts/init/init-logic-json-merge.ts +19 -0
- package/scripts/init/init-logic-secretlint.ts +7 -7
- package/scripts/init/init-logic-workspace.ts +41 -2
- package/scripts/init/init-logic.ts +16 -26
- package/scripts/init/init.ts +10 -16
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/package-manager-pin.ts +35 -0
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +32 -7
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +72 -22
- package/scripts/issue/issue-file.ts +29 -1
- package/scripts/issue/issue-labels.ts +13 -0
- package/scripts/issue/issue-release-cli.ts +155 -0
- package/scripts/issue/issue-release.ts +58 -0
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +14 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +18 -0
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-issue.ts +8 -1
- package/scripts/josh/josh-commands-lane.ts +24 -1
- package/scripts/josh/josh-commands-maintenance.ts +6 -6
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +34 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +12 -4
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +15 -1
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +44 -30
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-open.ts +5 -5
- package/scripts/lane/lane-paths.ts +13 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-registry.ts +11 -0
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-stray.ts +140 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/release/github-release-environment.ts +1 -0
- package/scripts/release/github-release-runs.ts +115 -0
- package/scripts/release/github-release.ts +13 -67
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +1 -1
- package/scripts/rules/protected-files.ts +59 -4
- package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
- package/scripts/rules/shell-body-trigger.ts +1 -1
- package/scripts/rules/stop-rules.ts +18 -7
- package/scripts/rules/test-declared-commit.ts +4 -4
- package/scripts/rules/wip-cap.ts +63 -0
- package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
- package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
- package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
- package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
- package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
- package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +7 -4
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
- package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
- package/scripts/run/ship/run-ship-lock.ts +50 -0
- package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
- package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
- package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
- package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
- package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
- package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
- package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
- package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
- package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/unit-worker-share.ts +1 -0
- package/scripts/time-runtime/time-background.ts +5 -1
- package/scripts/time-runtime/time-instant.ts +3 -1
- package/scripts/time-runtime/time-spans.ts +6 -139
- package/scripts/time-runtime/time-tool-call.ts +160 -0
- package/scripts/version/effective-upstream.ts +2 -20
- package/scripts/version/latest-corepack.ts +124 -93
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +6 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
- /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
- /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
- /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
- /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
- /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
- /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
- /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
- /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
- /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
- /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
- /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
- /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
- /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
- /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
- /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
- /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
- /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
|
@@ -5,6 +5,7 @@ import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
|
5
5
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
6
6
|
import { josh_environment_file } from '#scripts/josh/josh-environment-file'
|
|
7
7
|
import { lane_capacity } from '#scripts/lane/lane-capacity'
|
|
8
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
8
9
|
import { issue_citation } from '#scripts/rules/issue-citation'
|
|
9
10
|
import { epic_classify } from './epic-classify'
|
|
10
11
|
import { epic_cross_repo } from './epic-cross-repo'
|
|
@@ -31,12 +32,10 @@ import { epic_report, type EpicNextResult } from './epic-report'
|
|
|
31
32
|
const SUCCESS_EXIT_CODE = 0
|
|
32
33
|
const FAILURE_EXIT_CODE = 1
|
|
33
34
|
const ARGV_OFFSET = 2
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
// changed
|
|
37
|
-
|
|
38
|
-
const LANES_FLAG = '--lanes'
|
|
39
|
-
const FLAG_PREFIX = '--'
|
|
35
|
+
// Asking for every free lane (`--lanes`) is opt-in, so the answer stays one token for a caller that
|
|
36
|
+
// has not been changed. `--repo` alone still prints exactly one number — what joshuafolkken/kit#1491
|
|
37
|
+
// changed for it is *when* that number appears, not how many arrive (joshuafolkken/kit#1491).
|
|
38
|
+
const OPTIONS = { repo: { type: 'string' }, lanes: { type: 'boolean' } } as const
|
|
40
39
|
const USAGE =
|
|
41
40
|
'Usage: josh epic:next <epic-number|owner/repo#number>... [--repo <owner/repo>] [--lanes]'
|
|
42
41
|
// A repository that could not be read is refused rather than stood in for. Since
|
|
@@ -66,30 +65,6 @@ interface NextOptions {
|
|
|
66
65
|
usage?: string
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
// A value that is itself a flag is no value at all. `--repo --lanes` used to narrow to a repository
|
|
70
|
-
// literally named `--lanes` and report `No runnable child in --lanes` with exit 0; refused here, it
|
|
71
|
-
// reaches the usage line instead — the caller meant a repository and named none.
|
|
72
|
-
function parse_repo(rest: ReadonlyArray<string>): string | undefined {
|
|
73
|
-
const flag_index = rest.indexOf(REPO_FLAG)
|
|
74
|
-
const value = flag_index === -1 ? undefined : rest[flag_index + 1]
|
|
75
|
-
|
|
76
|
-
return value?.startsWith(FLAG_PREFIX) === true ? undefined : value
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// The leading arguments are epic references and everything from the first flag on is options. Split
|
|
80
|
-
// on the flag rather than taking a fixed count, so `epic:next 858 909 --repo X` reads two epics and
|
|
81
|
-
// `epic:next 858 --repo X` still reads one (joshuafolkken/kit#1493).
|
|
82
|
-
function split_at_flag(argv: ReadonlyArray<string>): {
|
|
83
|
-
head: ReadonlyArray<string>
|
|
84
|
-
rest: ReadonlyArray<string>
|
|
85
|
-
} {
|
|
86
|
-
const flag_at = argv.findIndex((entry) => entry.startsWith(FLAG_PREFIX))
|
|
87
|
-
|
|
88
|
-
return flag_at === -1
|
|
89
|
-
? { head: argv, rest: [] }
|
|
90
|
-
: { head: argv.slice(0, flag_at), rest: argv.slice(flag_at) }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
68
|
// Every leading entry, or nothing. One entry that does not parse fails the whole read rather than
|
|
94
69
|
// being dropped: a mistyped second epic would otherwise run the first one alone and report nothing
|
|
95
70
|
// at all about the one that was missed — an unattended run silently doing half of what was asked.
|
|
@@ -106,21 +81,34 @@ function parse_references(head: ReadonlyArray<string>): ReadonlyArray<EpicRefere
|
|
|
106
81
|
return references.length === 0 ? undefined : references
|
|
107
82
|
}
|
|
108
83
|
|
|
84
|
+
function with_repo(
|
|
85
|
+
references: ReadonlyArray<EpicReference>,
|
|
86
|
+
values: { repo?: string; lanes?: boolean },
|
|
87
|
+
): NextOptions {
|
|
88
|
+
const { repo, lanes: is_all_lanes = false } = values
|
|
89
|
+
|
|
90
|
+
if (repo === undefined) return is_all_lanes ? { usage: USAGE } : { references }
|
|
91
|
+
|
|
92
|
+
return { references, repo, is_all_lanes }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The positional arguments are epic references and the flags are options, so `epic:next 858 909 --repo
|
|
96
|
+
// X` reads two epics and `epic:next 858 --repo X` still reads one (joshuafolkken/kit#1493). The read is
|
|
97
|
+
// strict: an unknown flag reaches the usage line rather than being ignored, and a value that is itself
|
|
98
|
+
// a flag is no value at all — `--repo --lanes` used to narrow to a repository literally named
|
|
99
|
+
// `--lanes` and report `No runnable child in --lanes` with exit 0.
|
|
100
|
+
//
|
|
109
101
|
// `--lanes` without `--repo` is refused rather than ignored: the lane count is a property of one
|
|
110
102
|
// repository, so the aggregate listing has nothing to apply it to, and a flag that silently does
|
|
111
|
-
// nothing is a caller believing it asked for something.
|
|
103
|
+
// nothing is a caller believing it asked for something. An empty name (`--repo=` from an unset
|
|
104
|
+
// variable) is no repository either: narrowed to it, the answer is `No runnable child in .` and exit 0.
|
|
112
105
|
function parse_options(argv: ReadonlyArray<string>): NextOptions {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const is_all_lanes = rest.includes(LANES_FLAG)
|
|
118
|
-
|
|
119
|
-
if (repo === undefined) {
|
|
120
|
-
return is_all_lanes || rest.includes(REPO_FLAG) ? { usage: USAGE } : { references }
|
|
121
|
-
}
|
|
106
|
+
const parsed = cli_flags.arguments_of(argv, OPTIONS)
|
|
107
|
+
if (parsed === undefined) return { usage: USAGE }
|
|
108
|
+
const references = parse_references(parsed.positionals)
|
|
109
|
+
if (references === undefined || parsed.values.repo === '') return { usage: USAGE }
|
|
122
110
|
|
|
123
|
-
return
|
|
111
|
+
return with_repo(references, parsed.values)
|
|
124
112
|
}
|
|
125
113
|
|
|
126
114
|
// The answer for one epic, from an already-fetched snapshot. Split from the fetch so the whole
|
|
@@ -446,7 +434,6 @@ const epic_next = {
|
|
|
446
434
|
is_order_declared,
|
|
447
435
|
repo_verdict,
|
|
448
436
|
UNCHECKED_EXCLUSION,
|
|
449
|
-
split_at_flag,
|
|
450
437
|
parse_options,
|
|
451
438
|
refuse_reads,
|
|
452
439
|
views_of,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
2
|
-
import type { OpenIssueData } from '#scripts/git/schemas'
|
|
2
|
+
import type { OpenIssueData } from '#scripts/git/git-schemas'
|
|
3
3
|
import { has_any_label, IN_PROGRESS_LABEL, RUN_SOLO_LABEL } from '#scripts/issue/issue-labels'
|
|
4
4
|
import { lane_await } from '#scripts/lane/lane-await'
|
|
5
5
|
import { lane_registry } from '#scripts/lane/lane-registry'
|
|
@@ -20,12 +20,16 @@ type TriageVerdict = 'triage'
|
|
|
20
20
|
|
|
21
21
|
const TRIAGE_VERDICT: TriageVerdict = 'triage'
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
// Read off a bare label list too, so `josh issue:file` refuses an untriaged `auto-ok` filing by the
|
|
24
|
+
// same answer the offer paths withhold it by (joshuafolkken/kit#3313).
|
|
25
|
+
function has_triage_label(labels: ReadonlyArray<string>): boolean {
|
|
26
26
|
return has_label_name(labels, RUN_SOLO_LABEL) || has_label_name(labels, RUN_LANE_LABEL)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function is_triaged(child: EpicChild): boolean {
|
|
30
|
+
return has_triage_label(child.labels)
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
// The candidates nobody has judged, in the order they were given.
|
|
30
34
|
function untriaged(children: ReadonlyArray<EpicChild>): ReadonlyArray<EpicChild> {
|
|
31
35
|
return children.filter((child) => !is_triaged(child))
|
|
@@ -41,7 +45,7 @@ function message(children: ReadonlyArray<EpicChild>, repo: string): string {
|
|
|
41
45
|
)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
const epic_triage = { TRIAGE_VERDICT, is_triaged, message, untriaged }
|
|
48
|
+
const epic_triage = { TRIAGE_VERDICT, has_triage_label, is_triaged, message, untriaged }
|
|
45
49
|
|
|
46
50
|
export type { TriageVerdict }
|
|
47
51
|
export { epic_triage }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { epic_subject_schema } from '#scripts/git/git-schemas'
|
|
1
2
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
2
|
-
import { epic_subject_schema } from '#scripts/git/schemas'
|
|
3
3
|
import { EPIC_LABEL, has_label_name } from '#scripts/issue/issue-labels'
|
|
4
4
|
import { epic_parse, type DeclarationState } from './epic-parse'
|
|
5
5
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { git_next_issues } from '#scripts/issue/git-next-issues'
|
|
2
2
|
import { review_attest } from '#scripts/review/review-attest'
|
|
3
3
|
import { review_stamps } from '#scripts/review/review-stamps'
|
|
4
|
-
import { run_hold } from '#scripts/run/run-hold'
|
|
5
|
-
import { run_progress_clock } from '#scripts/run/run-progress-clock'
|
|
4
|
+
import { run_hold } from '#scripts/run/hold/run-hold'
|
|
5
|
+
import { run_progress_clock } from '#scripts/run/progress/run-progress-clock'
|
|
6
6
|
import { parse_completed_issue_number } from './followup-issue-number'
|
|
7
7
|
import { git_followup_cleanup, type CleanupStep } from './git-followup-cleanup'
|
|
8
8
|
import { git_followup_pending } from './git-followup-pending'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { issue_label_schema } from '#scripts/git/git-schemas'
|
|
2
3
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { issue_label_schema } from '#scripts/git/schemas'
|
|
4
4
|
import { IN_PROGRESS_LABEL, label_name_of } from '#scripts/issue/issue-labels'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ import { z } from 'zod'
|
|
|
19
19
|
// No procedure covered the ordinary ending: merged, closed, and still marked as running.
|
|
20
20
|
|
|
21
21
|
// The `number,labels,body` read answers labels as objects, and this is the one field of it this
|
|
22
|
-
// step needs. The element shape is `schemas.ts`'s rather than a second spelling of it; the wrapper
|
|
22
|
+
// step needs. The element shape is `git-schemas.ts`'s rather than a second spelling of it; the wrapper
|
|
23
23
|
// is local because a read's *field list* is what multiplies helpers when it is shared
|
|
24
24
|
// (`git-gh-issue-read.ts`).
|
|
25
25
|
const labels_read_schema = z.object({ labels: z.array(issue_label_schema).optional() })
|
|
@@ -66,8 +66,8 @@ function is_posted_since(comment: IssueComment, merged_at: string): boolean {
|
|
|
66
66
|
return Date.parse(comment.created_at) >= Date.parse(merged_at)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function parse_comments(comments_json: string | undefined): Array<IssueComment> {
|
|
70
|
-
const parsed = COMMENTS_SCHEMA.safeParse(json_value.parse_or_undefined(comments_json
|
|
69
|
+
function parse_comments(comments_json: string | undefined = ''): Array<IssueComment> {
|
|
70
|
+
const parsed = COMMENTS_SCHEMA.safeParse(json_value.parse_or_undefined(comments_json))
|
|
71
71
|
|
|
72
72
|
return parsed.success ? parsed.data : []
|
|
73
73
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { git_gh_helpers } from '#scripts/gh/git-gh-helpers'
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_STABLE_READS,
|
|
5
|
+
git_pr_checks,
|
|
6
|
+
WATCH_CONFIRMED_STABLE_READS,
|
|
7
|
+
} from '#scripts/gh/git-pr-checks'
|
|
8
|
+
import { is_coderabbit_check } from '#scripts/gh/git-pr-checks-eval'
|
|
9
|
+
import { CHECK_STATUS_PASS, type PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
|
|
10
|
+
import { json_value } from '#scripts/lib/json-value'
|
|
11
|
+
|
|
12
|
+
// The check wait and the notes read off its snapshot — split out of `git-pr-followup.ts` when it
|
|
13
|
+
// reached its line limit (joshuafolkken/kit#3264). `git-pr-followup` re-exports each name it always
|
|
14
|
+
// exported, so the move changed no call site and no suite that imports from it.
|
|
15
|
+
|
|
16
|
+
// Named so a test can pin the note without restating it (joshuafolkken/kit#999).
|
|
17
|
+
const WATCH_FAILED_NOTE = 'pr checks --watch failed; falling through to polling'
|
|
18
|
+
|
|
19
|
+
// The watch is a look ahead, not a gate. It fails when **any** check has failed — CodeRabbit
|
|
20
|
+
// included — and letting that escape ended the run before `evaluate_pr_state` could apply kit#753's
|
|
21
|
+
// CodeRabbit exemption at all. It only ever bit where every check finished inside the two-minute
|
|
22
|
+
// window, so a repository with a slow E2E never saw it and a fast one always would
|
|
23
|
+
// (joshuafolkken/kit#999). What the watch is has since changed — `gh pr checks --watch` went through
|
|
24
|
+
// GraphQL, so joshuafolkken/kit#1028 replaced it with the same poll loop bounded to two minutes —
|
|
25
|
+
// but its place here has not.
|
|
26
|
+
//
|
|
27
|
+
// Falling through costs nothing: whether the merge may proceed is decided in one place below, and a
|
|
28
|
+
// genuinely failing non-CodeRabbit check still ends the wait on the first poll by kit#990's
|
|
29
|
+
// fast-fail rather than by that failure.
|
|
30
|
+
// The watch fails for two different reasons — "a check failed" and "no checks reported on this
|
|
31
|
+
// branch" — and its own error does not say which. The pull request itself can: a failed check leaves
|
|
32
|
+
// a rollup, and a branch with no checks leaves it empty. Falling through on the empty case would
|
|
33
|
+
// trade a failure reported in seconds for the whole budget spent waiting on a required check that is
|
|
34
|
+
// missing rather than pending, so that one is rethrown and the old behavior kept.
|
|
35
|
+
//
|
|
36
|
+
// **Read from the raw payload, not from `parse_pr_state_snapshot`.** That parser never throws: a
|
|
37
|
+
// malformed answer or a schema mismatch degrades to `rollup: []`, which is indistinguishable there
|
|
38
|
+
// from a branch that genuinely has no checks. Rethrowing on it would put an unreadable answer back
|
|
39
|
+
// on the path this whole change exists to remove. So the question asked here is the narrow one —
|
|
40
|
+
// *is this definitely an empty rollup* — and every other outcome falls through.
|
|
41
|
+
function reads_as_empty_rollup(raw_json: string): boolean {
|
|
42
|
+
const parsed = json_value.parse_or_undefined(raw_json)
|
|
43
|
+
|
|
44
|
+
if (typeof parsed !== 'object' || parsed === null || !('statusCheckRollup' in parsed)) {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { statusCheckRollup: rollup } = parsed
|
|
49
|
+
|
|
50
|
+
return Array.isArray(rollup) && rollup.length === 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The read throws rather than answering `undefined`, so the `catch` is its only failure path.
|
|
54
|
+
//
|
|
55
|
+
// The **checks** half, not the whole snapshot: the question here is about `statusCheckRollup` alone,
|
|
56
|
+
// and reading the review listing to answer it paged a whole conversation for nothing
|
|
57
|
+
// (joshuafolkken/kit#1043).
|
|
58
|
+
async function has_no_checks(branch_name: string): Promise<boolean> {
|
|
59
|
+
try {
|
|
60
|
+
const checks = await git_gh_command.pr_get_checks_snapshot(branch_name)
|
|
61
|
+
|
|
62
|
+
return reads_as_empty_rollup(checks.snapshot_json)
|
|
63
|
+
} catch {
|
|
64
|
+
// The read refines the swallow; it is not a gate of its own. An answer it cannot get is not
|
|
65
|
+
// evidence of anything, so prefer falling through — the poll below reads the same state.
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function handle_watch_failure(branch_name: string, error: unknown): Promise<void> {
|
|
71
|
+
if (await has_no_checks(branch_name)) throw error
|
|
72
|
+
|
|
73
|
+
// Swallowed, but never silently: the reason a run stopped early used to be this line, so it
|
|
74
|
+
// stays visible even though it no longer decides anything.
|
|
75
|
+
console.info(`⚠️ ${WATCH_FAILED_NOTE}: ${git_gh_helpers.get_error_message_with_stderr(error)}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// **Returns whether the watch confirmed completion** (joshuafolkken/kit#2029): `true` only when
|
|
79
|
+
// `pr_checks_watch` saw every check finish, `false` when it timed out with checks still pending or
|
|
80
|
+
// fell through to polling on a swallowed failure. `run_checks` reads it to decide how many stable poll
|
|
81
|
+
// reads the wait still needs. A timed-out watch (`timed_out: true`) never spanned the pending→settled
|
|
82
|
+
// window, so it must keep the full stable-read window rather than claim the confirmation. A rethrown
|
|
83
|
+
// failure — the empty-rollup case — never reaches a caller, so it needs no answer here.
|
|
84
|
+
async function watch_before_polling(branch_name: string): Promise<boolean> {
|
|
85
|
+
console.info('')
|
|
86
|
+
console.info('📊 Watching PR checks...')
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const result = await git_gh_command.pr_checks_watch(branch_name)
|
|
90
|
+
|
|
91
|
+
return !result.timed_out
|
|
92
|
+
} catch (error) {
|
|
93
|
+
await handle_watch_failure(branch_name, error)
|
|
94
|
+
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// **A confirmed watch lowers the poll's stable-read requirement to one** (joshuafolkken/kit#2029). The
|
|
100
|
+
// watch is the first confirmation that every check settled, so the poll that follows need only agree
|
|
101
|
+
// once rather than twice — which removes the extra interval `followup` spent re-proving a settled run.
|
|
102
|
+
// A skipped or fallen-through watch keeps `DEFAULT_STABLE_READS`, and a check that turned red is still
|
|
103
|
+
// caught: the poll evaluates the full merge gate and throws on failure before any stable read counts.
|
|
104
|
+
async function run_checks(input: {
|
|
105
|
+
branch_name: string
|
|
106
|
+
is_skip_watch: boolean
|
|
107
|
+
}): Promise<PrStateSnapshot> {
|
|
108
|
+
const is_watch_confirmed = !input.is_skip_watch && (await watch_before_polling(input.branch_name))
|
|
109
|
+
const stable_reads = is_watch_confirmed ? WATCH_CONFIRMED_STABLE_READS : DEFAULT_STABLE_READS
|
|
110
|
+
|
|
111
|
+
return await git_pr_checks.wait_for_pr_success(input.branch_name, stable_reads)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Temporary (kit#753): record every CodeRabbit check that was not passing when the merge gate
|
|
115
|
+
// opened, so a merge shipped without CodeRabbit review stays auditable.
|
|
116
|
+
function read_coderabbit_skip_notes(snapshot: PrStateSnapshot): Array<string> {
|
|
117
|
+
return snapshot.rollup
|
|
118
|
+
.filter((check) => is_coderabbit_check(check.name))
|
|
119
|
+
.filter((check) => check.status !== CHECK_STATUS_PASS)
|
|
120
|
+
.map(
|
|
121
|
+
(check) =>
|
|
122
|
+
`CodeRabbit check skipped (kit#753): ${check.name} was ${check.status} at merge time`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// The skip is printed where the run is being watched, not only carried into the Telegram body it
|
|
127
|
+
// ends up in. A merge that went ahead while CodeRabbit had not come back is the one thing kit#753's
|
|
128
|
+
// exemption trades away, and it used to be invisible in `followup`'s own output
|
|
129
|
+
// (joshuafolkken/kit#1217).
|
|
130
|
+
function log_skip_notes(notes: ReadonlyArray<string>): void {
|
|
131
|
+
for (const note of notes) {
|
|
132
|
+
console.info(`⏭ ${note}`)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const git_pr_followup_checks = {
|
|
137
|
+
run_checks,
|
|
138
|
+
read_coderabbit_skip_notes,
|
|
139
|
+
log_skip_notes,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { git_pr_followup_checks, WATCH_FAILED_NOTE }
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
-
import { git_gh_helpers } from '#scripts/gh/git-gh-helpers'
|
|
3
2
|
import { git_pr_ai_review, type TelegramContext } from '#scripts/gh/git-pr-ai-review'
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_STABLE_READS,
|
|
6
|
-
git_pr_checks,
|
|
7
|
-
WATCH_CONFIRMED_STABLE_READS,
|
|
8
|
-
} from '#scripts/gh/git-pr-checks'
|
|
9
|
-
import { is_coderabbit_check } from '#scripts/gh/git-pr-checks-eval'
|
|
10
|
-
import { CHECK_STATUS_PASS, type PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
|
|
11
3
|
import { git_pr_coderabbit } from '#scripts/gh/git-pr-coderabbit'
|
|
12
4
|
import { git_pr_managed_config } from '#scripts/gh/git-pr-managed-config'
|
|
13
5
|
import { github_issue_url } from '#scripts/gh/github-issue-url'
|
|
14
6
|
import { git_closes_keyword } from '#scripts/git/git-closes-keyword'
|
|
15
|
-
import { json_value } from '#scripts/lib/json-value'
|
|
16
7
|
import type { GitNotifyConfig } from '#scripts/notify/git-notify'
|
|
17
8
|
import {
|
|
18
9
|
telegram_notify,
|
|
@@ -22,12 +13,12 @@ import {
|
|
|
22
13
|
import { git_followup_merged } from './git-followup-merged'
|
|
23
14
|
import { git_followup_pending } from './git-followup-pending'
|
|
24
15
|
import { git_followup_stages, type StageLog } from './git-followup-stages'
|
|
16
|
+
import { git_pr_followup_checks } from './git-pr-followup-checks'
|
|
25
17
|
import { git_pr_followup_wrapup } from './git-pr-followup-wrapup'
|
|
26
18
|
|
|
27
19
|
const { STAGE, lap } = git_followup_stages
|
|
20
|
+
const { run_checks, read_coderabbit_skip_notes, log_skip_notes } = git_pr_followup_checks
|
|
28
21
|
|
|
29
|
-
// Named so a test can pin the note without restating it (joshuafolkken/kit#999).
|
|
30
|
-
const WATCH_FAILED_NOTE = 'pr checks --watch failed; falling through to polling'
|
|
31
22
|
const REPO_NAME_SEPARATOR = '/'
|
|
32
23
|
|
|
33
24
|
function parse_repo_name(name_with_owner: string | undefined): string | undefined {
|
|
@@ -107,123 +98,6 @@ async function warn_if_missing_closes(branch_name: string): Promise<string | und
|
|
|
107
98
|
return undefined
|
|
108
99
|
}
|
|
109
100
|
|
|
110
|
-
// The watch is a look ahead, not a gate. It fails when **any** check has failed — CodeRabbit
|
|
111
|
-
// included — and letting that escape ended the run before `evaluate_pr_state` could apply kit#753's
|
|
112
|
-
// CodeRabbit exemption at all. It only ever bit where every check finished inside the two-minute
|
|
113
|
-
// window, so a repository with a slow E2E never saw it and a fast one always would
|
|
114
|
-
// (joshuafolkken/kit#999). What the watch is has since changed — `gh pr checks --watch` went through
|
|
115
|
-
// GraphQL, so joshuafolkken/kit#1028 replaced it with the same poll loop bounded to two minutes —
|
|
116
|
-
// but its place here has not.
|
|
117
|
-
//
|
|
118
|
-
// Falling through costs nothing: whether the merge may proceed is decided in one place below, and a
|
|
119
|
-
// genuinely failing non-CodeRabbit check still ends the wait on the first poll by kit#990's
|
|
120
|
-
// fast-fail rather than by that failure.
|
|
121
|
-
// The watch fails for two different reasons — "a check failed" and "no checks reported on this
|
|
122
|
-
// branch" — and its own error does not say which. The pull request itself can: a failed check leaves
|
|
123
|
-
// a rollup, and a branch with no checks leaves it empty. Falling through on the empty case would
|
|
124
|
-
// trade a failure reported in seconds for the whole budget spent waiting on a required check that is
|
|
125
|
-
// missing rather than pending, so that one is rethrown and the old behavior kept.
|
|
126
|
-
//
|
|
127
|
-
// **Read from the raw payload, not from `parse_pr_state_snapshot`.** That parser never throws: a
|
|
128
|
-
// malformed answer or a schema mismatch degrades to `rollup: []`, which is indistinguishable there
|
|
129
|
-
// from a branch that genuinely has no checks. Rethrowing on it would put an unreadable answer back
|
|
130
|
-
// on the path this whole change exists to remove. So the question asked here is the narrow one —
|
|
131
|
-
// *is this definitely an empty rollup* — and every other outcome falls through.
|
|
132
|
-
function reads_as_empty_rollup(raw_json: string): boolean {
|
|
133
|
-
const parsed = json_value.parse_or_undefined(raw_json)
|
|
134
|
-
|
|
135
|
-
if (typeof parsed !== 'object' || parsed === null || !('statusCheckRollup' in parsed)) {
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const { statusCheckRollup: rollup } = parsed
|
|
140
|
-
|
|
141
|
-
return Array.isArray(rollup) && rollup.length === 0
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// The read throws rather than answering `undefined`, so the `catch` is its only failure path.
|
|
145
|
-
//
|
|
146
|
-
// The **checks** half, not the whole snapshot: the question here is about `statusCheckRollup` alone,
|
|
147
|
-
// and reading the review listing to answer it paged a whole conversation for nothing
|
|
148
|
-
// (joshuafolkken/kit#1043).
|
|
149
|
-
async function has_no_checks(branch_name: string): Promise<boolean> {
|
|
150
|
-
try {
|
|
151
|
-
const checks = await git_gh_command.pr_get_checks_snapshot(branch_name)
|
|
152
|
-
|
|
153
|
-
return reads_as_empty_rollup(checks.snapshot_json)
|
|
154
|
-
} catch {
|
|
155
|
-
// The read refines the swallow; it is not a gate of its own. An answer it cannot get is not
|
|
156
|
-
// evidence of anything, so prefer falling through — the poll below reads the same state.
|
|
157
|
-
return false
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async function handle_watch_failure(branch_name: string, error: unknown): Promise<void> {
|
|
162
|
-
if (await has_no_checks(branch_name)) throw error
|
|
163
|
-
|
|
164
|
-
// Swallowed, but never silently: the reason a run stopped early used to be this line, so it
|
|
165
|
-
// stays visible even though it no longer decides anything.
|
|
166
|
-
console.info(`⚠️ ${WATCH_FAILED_NOTE}: ${git_gh_helpers.get_error_message_with_stderr(error)}`)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// **Returns whether the watch confirmed completion** (joshuafolkken/kit#2029): `true` only when
|
|
170
|
-
// `pr_checks_watch` saw every check finish, `false` when it timed out with checks still pending or
|
|
171
|
-
// fell through to polling on a swallowed failure. `run_checks` reads it to decide how many stable poll
|
|
172
|
-
// reads the wait still needs. A timed-out watch (`timed_out: true`) never spanned the pending→settled
|
|
173
|
-
// window, so it must keep the full stable-read window rather than claim the confirmation. A rethrown
|
|
174
|
-
// failure — the empty-rollup case — never reaches a caller, so it needs no answer here.
|
|
175
|
-
async function watch_before_polling(branch_name: string): Promise<boolean> {
|
|
176
|
-
console.info('')
|
|
177
|
-
console.info('📊 Watching PR checks...')
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const result = await git_gh_command.pr_checks_watch(branch_name)
|
|
181
|
-
|
|
182
|
-
return !result.timed_out
|
|
183
|
-
} catch (error) {
|
|
184
|
-
await handle_watch_failure(branch_name, error)
|
|
185
|
-
|
|
186
|
-
return false
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// **A confirmed watch lowers the poll's stable-read requirement to one** (joshuafolkken/kit#2029). The
|
|
191
|
-
// watch is the first confirmation that every check settled, so the poll that follows need only agree
|
|
192
|
-
// once rather than twice — which removes the extra interval `followup` spent re-proving a settled run.
|
|
193
|
-
// A skipped or fallen-through watch keeps `DEFAULT_STABLE_READS`, and a check that turned red is still
|
|
194
|
-
// caught: the poll evaluates the full merge gate and throws on failure before any stable read counts.
|
|
195
|
-
async function run_checks(input: {
|
|
196
|
-
branch_name: string
|
|
197
|
-
is_skip_watch: boolean
|
|
198
|
-
}): Promise<PrStateSnapshot> {
|
|
199
|
-
const is_watch_confirmed = !input.is_skip_watch && (await watch_before_polling(input.branch_name))
|
|
200
|
-
const stable_reads = is_watch_confirmed ? WATCH_CONFIRMED_STABLE_READS : DEFAULT_STABLE_READS
|
|
201
|
-
|
|
202
|
-
return await git_pr_checks.wait_for_pr_success(input.branch_name, stable_reads)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Temporary (kit#753): record every CodeRabbit check that was not passing when the merge gate
|
|
206
|
-
// opened, so a merge shipped without CodeRabbit review stays auditable.
|
|
207
|
-
function read_coderabbit_skip_notes(snapshot: PrStateSnapshot): Array<string> {
|
|
208
|
-
return snapshot.rollup
|
|
209
|
-
.filter((check) => is_coderabbit_check(check.name))
|
|
210
|
-
.filter((check) => check.status !== CHECK_STATUS_PASS)
|
|
211
|
-
.map(
|
|
212
|
-
(check) =>
|
|
213
|
-
`CodeRabbit check skipped (kit#753): ${check.name} was ${check.status} at merge time`,
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// The skip is printed where the run is being watched, not only carried into the Telegram body it
|
|
218
|
-
// ends up in. A merge that went ahead while CodeRabbit had not come back is the one thing kit#753's
|
|
219
|
-
// exemption trades away, and it used to be invisible in `followup`'s own output
|
|
220
|
-
// (joshuafolkken/kit#1217).
|
|
221
|
-
function log_skip_notes(notes: ReadonlyArray<string>): void {
|
|
222
|
-
for (const note of notes) {
|
|
223
|
-
console.info(`⏭ ${note}`)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
101
|
// **The one read of the four that can depend on another.** Without an issue number on the command
|
|
228
102
|
// line it is the pull request body that names it (joshuafolkken/kit#1539), so the title read waits
|
|
229
103
|
// for exactly that one and for nothing else. With a number in hand the `??` short-circuits and the
|
|
@@ -451,7 +325,6 @@ const git_pr_followup = {
|
|
|
451
325
|
|
|
452
326
|
export {
|
|
453
327
|
git_pr_followup,
|
|
454
|
-
WATCH_FAILED_NOTE,
|
|
455
328
|
run_checks,
|
|
456
329
|
build_issue_url,
|
|
457
330
|
parse_repo_name,
|
|
@@ -464,3 +337,5 @@ export {
|
|
|
464
337
|
}
|
|
465
338
|
export type { FollowupInput }
|
|
466
339
|
export type { TelegramContext } from '#scripts/gh/git-pr-ai-review'
|
|
340
|
+
|
|
341
|
+
export { WATCH_FAILED_NOTE } from './git-pr-followup-checks'
|