@joshuafolkken/kit 1.1784.0 → 1.2114.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 +59 -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 +415 -389
- 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 +15 -8
- 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,22 +1,31 @@
|
|
|
1
1
|
import { git_gh_pr_read } from '#scripts/gh/git-gh-pr-read'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
FAILURE_MESSAGE_PREFIX,
|
|
4
|
+
FAILURE_REASON_CHECKS,
|
|
5
|
+
FAILURE_REASON_CONFLICT,
|
|
6
|
+
} from '#scripts/gh/git-pr-checks-eval'
|
|
3
7
|
import { git_command } from '#scripts/git/git-command'
|
|
4
8
|
import { main_merge, type MergeOutcome } from '#scripts/git/main-merge'
|
|
9
|
+
import { pre_commit_lint_switch } from '#scripts/hooks/pre-commit-lint-switch'
|
|
10
|
+
import { pre_push_related_switch } from '#scripts/hooks/pre-push-related-switch'
|
|
5
11
|
import { josh_command, type JoshResult } from '#scripts/josh/josh-run'
|
|
6
12
|
import { error_text } from '#scripts/lib/error-message'
|
|
13
|
+
import { run_ship_lap } from './run-ship-lap'
|
|
7
14
|
import { run_ship_overlap } from './run-ship-overlap'
|
|
8
15
|
import { run_ship_scoped } from './run-ship-scoped'
|
|
9
|
-
import type
|
|
16
|
+
import { run_ship_stop_cause, type Cause } from './run-ship-stop-cause'
|
|
10
17
|
|
|
11
18
|
// `josh ship` keeps the branch current with the default branch itself: a default branch that moves
|
|
12
19
|
// while the pull request opens and CI runs would otherwise stop the followup as `interrupted`, though
|
|
13
20
|
// merging the default branch again usually resolves it.
|
|
14
21
|
//
|
|
15
|
-
// So the merge happens mechanically:
|
|
16
|
-
//
|
|
17
|
-
//
|
|
22
|
+
// So the merge happens mechanically: before the gate (`sync_stage`), again before the push when the
|
|
23
|
+
// default branch moved since (`catch_up`), and when the followup fails after it moved again
|
|
24
|
+
// (`followup_stage`). A conflict only on
|
|
25
|
+
// the files `main-merge-regenerate.ts` lists is resolved by the merge itself; any other conflict
|
|
18
26
|
// stops the ship as `conflict`, naming the unmerged paths so the resumed session goes straight to them.
|
|
19
27
|
|
|
28
|
+
const { STEP, timed } = run_ship_lap
|
|
20
29
|
const should_forward_stderr = true
|
|
21
30
|
const SUCCESS_EXIT_CODE = 0
|
|
22
31
|
const FAILURE_EXIT_CODE = 1
|
|
@@ -27,19 +36,32 @@ const CONFLICT_NOTE = 'merging the default branch left unmerged paths:'
|
|
|
27
36
|
const DEFERRED_PREFIX = 'merge deferred to the followup: '
|
|
28
37
|
const CURRENT_SUFFIX = ' brings nothing in'
|
|
29
38
|
const MERGED_PREFIX = 'merged '
|
|
39
|
+
const MERGED_GATE_NOTE =
|
|
40
|
+
'the default branch moved since the sync; the tree merged with it fails the gate:'
|
|
41
|
+
const PUSHED_NOTE = 'pushed the merge ahead of the gate, so CI runs beside it'
|
|
42
|
+
const PUSH_FAILED_NOTE =
|
|
43
|
+
'the push ahead of the gate failed; the commit stage pushes after the gate:'
|
|
30
44
|
|
|
31
45
|
// A stage's result, plus the paths a stopped merge left unmerged — carried to the stop prompt.
|
|
32
46
|
// `is_skipped` marks a stage that found nothing to do — a review round with nothing to review, a gate
|
|
33
47
|
// that reused a green record — so its few seconds are not timed as a run of it. `preconditions` names
|
|
34
48
|
// the kinds of the preconditions a stopped preflight found unmet (`run-ship-preflight.ts`). `cause` is the
|
|
35
|
-
// one a stopped
|
|
49
|
+
// one a stopped stage tagged its stop with — a review round, or a red gate over a merged tree
|
|
50
|
+
// (`run-ship-stop-cause.ts`). `reason` says why the stage ran or was skipped, written on its event line
|
|
51
|
+
// (`run-ship-review-reason.ts`).
|
|
36
52
|
interface StepResult extends JoshResult {
|
|
37
53
|
is_skipped?: boolean
|
|
54
|
+
reason?: string
|
|
38
55
|
conflicts?: ReadonlyArray<string>
|
|
39
56
|
preconditions?: ReadonlyArray<string>
|
|
40
57
|
cause?: Cause
|
|
41
58
|
}
|
|
42
59
|
|
|
60
|
+
// The push of a tree already committed, with no pull request touched.
|
|
61
|
+
function push_argv(title: string): ReadonlyArray<string> {
|
|
62
|
+
return ['git', '-y', '--skip-commit', '--skip-pr', title]
|
|
63
|
+
}
|
|
64
|
+
|
|
43
65
|
async function josh(argv: ReadonlyArray<string>): Promise<JoshResult> {
|
|
44
66
|
return await josh_command.josh_run(argv, should_forward_stderr)
|
|
45
67
|
}
|
|
@@ -48,7 +70,7 @@ async function josh(argv: ReadonlyArray<string>): Promise<JoshResult> {
|
|
|
48
70
|
// changed, and the followup still finds a real conflict on GitHub's side.
|
|
49
71
|
async function merge_outcome(): Promise<MergeOutcome> {
|
|
50
72
|
try {
|
|
51
|
-
return await main_merge.merge()
|
|
73
|
+
return await timed(STEP.MERGE, async () => await main_merge.merge())
|
|
52
74
|
} catch (error) {
|
|
53
75
|
return { kind: 'refused', message: error_text.message_of(error) }
|
|
54
76
|
}
|
|
@@ -68,51 +90,161 @@ function sync_note(outcome: Exclude<MergeOutcome, { kind: 'conflict' }>): string
|
|
|
68
90
|
: `${MERGED_PREFIX}${outcome.branch}`
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
93
|
+
// Uncommitted work is committed locally first, never stashed: the merge guard refuses uncommitted work
|
|
94
|
+
// the default branch also touches, and a merge deferred past it met its conflict only after the gate,
|
|
95
|
+
// both review rounds and CI (joshuafolkken/kit#4708). The commit is not pushed here: a tree the sync
|
|
96
|
+
// leaves unmerged goes out at the commit stage once the gate is green (`push_ahead` pushes a merged
|
|
97
|
+
// one). A refusal left — a fetch that failed — does not stop the ship: the commit stage and the
|
|
98
|
+
// followup merge again.
|
|
99
|
+
// The pre-commit prettier, cspell and eslint are switched off for it: the gate or the scoped pair runs
|
|
100
|
+
// the same checks over this tree before anything is pushed, and the hooks only waited for the core
|
|
101
|
+
// budget to repeat them (joshuafolkken/kit#4985).
|
|
102
|
+
async function commit_locally(title: string, is_dirty: boolean): Promise<JoshResult | undefined> {
|
|
103
|
+
if (!is_dirty) return undefined
|
|
104
|
+
|
|
105
|
+
const commit = await timed(
|
|
106
|
+
STEP.LOCAL_COMMIT,
|
|
107
|
+
async () =>
|
|
108
|
+
await josh_command.josh_run(
|
|
109
|
+
['git', '-y', '--skip-push', '--skip-pr', title],
|
|
110
|
+
should_forward_stderr,
|
|
111
|
+
undefined,
|
|
112
|
+
{ [pre_commit_lint_switch.SWITCH_ENV_KEY]: 'off' },
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return commit.code === SUCCESS_EXIT_CODE ? undefined : commit
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// **A merge the sync made is pushed at once onto an open pull request, ahead of the gate**
|
|
120
|
+
// (joshuafolkken/kit#5067): CI and SonarQube start from this push, and pushed after the gate they ran
|
|
121
|
+
// behind it, leaving the followup minutes of waiting. Only the pull request's own branch moves; the
|
|
122
|
+
// merge still needs the gate, the review attest and a green CI, and a red gate stops the ship before
|
|
123
|
+
// the followup as before, the next push replacing the commit. Its pre-push hook runs the related tests
|
|
124
|
+
// (`pre-push-related-switch.ts`), since the gate over this same tree runs the whole suite next. A
|
|
125
|
+
// branch with no open pull request pushes nothing here: CI runs on pull requests only. An unmoved
|
|
126
|
+
// default branch pushes nothing either, so the commit stage stays the one push; a merged one makes it
|
|
127
|
+
// two, and CI and the review bots run again on the second. A failed push stops nothing: the commit
|
|
128
|
+
// stage pushes the gated tree.
|
|
129
|
+
async function push_ahead(title: string): Promise<string> {
|
|
130
|
+
const push = await timed(
|
|
131
|
+
STEP.PUSH,
|
|
132
|
+
async () =>
|
|
133
|
+
await josh_command.josh_run(push_argv(title), should_forward_stderr, undefined, {
|
|
134
|
+
[pre_push_related_switch.SWITCH_ENV_KEY]: pre_push_related_switch.SWITCH_ON,
|
|
135
|
+
}),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return push.code === SUCCESS_EXIT_CODE ? PUSHED_NOTE : [PUSH_FAILED_NOTE, push.out].join('\n')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// An unreadable pull request reads as none: the commit stage pushes after the gate either way.
|
|
142
|
+
async function has_open_pull(): Promise<boolean> {
|
|
143
|
+
try {
|
|
144
|
+
return (await git_gh_pr_read.pr_get_number(await git_command.branch())) !== undefined
|
|
145
|
+
} catch {
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function pushed_note(outcome: MergeOutcome, title: string): Promise<string> {
|
|
151
|
+
if (outcome.kind !== 'merged' || !(await has_open_pull())) return ''
|
|
152
|
+
|
|
153
|
+
return await push_ahead(title)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function waited_note(issue: string): Promise<string> {
|
|
157
|
+
const overlap = await timed(
|
|
158
|
+
STEP.OVERLAP_WAIT,
|
|
159
|
+
async () => await run_ship_overlap.await_overlap(issue),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return run_ship_overlap.note(overlap)
|
|
163
|
+
}
|
|
164
|
+
|
|
76
165
|
// The merge waits first for any lane whose open pull request changes the same paths
|
|
77
166
|
// (`run-ship-overlap.ts`), so it brings that pull request in before the gate rather than meeting it
|
|
78
167
|
// as `DIRTY` after CI.
|
|
79
|
-
async function sync_stage(issue: string): Promise<StepResult> {
|
|
80
|
-
const waited =
|
|
168
|
+
async function sync_stage(issue: string, title: string, is_dirty: boolean): Promise<StepResult> {
|
|
169
|
+
const waited = await waited_note(issue)
|
|
170
|
+
const failed_commit = await commit_locally(title, is_dirty)
|
|
171
|
+
|
|
172
|
+
if (failed_commit !== undefined) {
|
|
173
|
+
const out = [waited, failed_commit.out].filter((line) => line !== '').join('\n')
|
|
174
|
+
|
|
175
|
+
return { ...failed_commit, out }
|
|
176
|
+
}
|
|
177
|
+
|
|
81
178
|
const outcome = await merge_outcome()
|
|
82
179
|
|
|
83
180
|
if (outcome.kind === 'conflict') return conflict_result(outcome.files, waited)
|
|
84
181
|
|
|
85
|
-
const
|
|
182
|
+
const pushed = await pushed_note(outcome, title)
|
|
183
|
+
const out = [waited, sync_note(outcome), pushed].filter((line) => line !== '').join('\n')
|
|
86
184
|
|
|
87
185
|
return { code: SUCCESS_EXIT_CODE, out }
|
|
88
186
|
}
|
|
89
187
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
188
|
+
// The tree merged with a default branch that moved is gated before it is pushed: a tree only the
|
|
189
|
+
// combined change breaks — two lanes each growing one document inside its cap — stops here, reproduced
|
|
190
|
+
// locally rather than met on CI's merge ref (joshuafolkken/kit#4825). The push then carries the tree
|
|
191
|
+
// the gate's record describes, so the pre-push hook reuses it rather than re-running the unit suite.
|
|
192
|
+
// Its red is tagged a check, so the stage's infrastructure retry never reads test output as an outage.
|
|
193
|
+
async function gate_merged(): Promise<StepResult | undefined> {
|
|
194
|
+
const gate = await run_ship_scoped.scoped_gate()
|
|
94
195
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
196
|
+
if (gate.code === SUCCESS_EXIT_CODE) return undefined
|
|
197
|
+
|
|
198
|
+
const out = [MERGED_GATE_NOTE, gate.out].filter((line) => line !== '').join('\n')
|
|
199
|
+
|
|
200
|
+
return { ...gate, out, cause: run_ship_stop_cause.CAUSE.CHECK }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Before the commit stage pushes: a default branch that moved since the sync stage is merged again and
|
|
204
|
+
// the merged tree gated, so the stop lands while the lane still waits on the ship. An unmoved one
|
|
205
|
+
// merges nothing and re-runs no gate; a refusal leaves the merge to the followup, as the sync does.
|
|
206
|
+
// A tree with no green gate record is gated whatever the merge answered: a re-run commit stage finds
|
|
207
|
+
// the merge its red gate stopped on already made, and must not push it without a gate.
|
|
208
|
+
async function catch_up(): Promise<StepResult | undefined> {
|
|
209
|
+
const outcome = await merge_outcome()
|
|
210
|
+
|
|
211
|
+
if (outcome.kind === 'conflict') return conflict_result(outcome.files, '')
|
|
212
|
+
if (outcome.kind !== 'merged' && (await run_ship_scoped.is_gate_green())) return undefined
|
|
213
|
+
|
|
214
|
+
return await gate_merged()
|
|
99
215
|
}
|
|
100
216
|
|
|
101
|
-
// The merged tree is gated before it is pushed: the push then carries the tree
|
|
102
|
-
// the gate's record describes, so the pre-push hook reuses it rather than re-running the unit suite.
|
|
103
217
|
async function push_merged(title: string): Promise<StepResult | undefined> {
|
|
104
|
-
const gate = await
|
|
218
|
+
const gate = await gate_merged()
|
|
105
219
|
|
|
106
|
-
if (gate
|
|
220
|
+
if (gate !== undefined) return gate
|
|
107
221
|
|
|
108
|
-
const push = await josh(
|
|
222
|
+
const push = await josh(push_argv(title))
|
|
109
223
|
|
|
110
224
|
return push.code === SUCCESS_EXIT_CODE ? undefined : push
|
|
111
225
|
}
|
|
112
226
|
|
|
227
|
+
// The followup's reasons a newer base can change: a red check or a conflict.
|
|
228
|
+
const MERGE_FIXABLE_REASONS = [FAILURE_REASON_CHECKS, FAILURE_REASON_CONFLICT]
|
|
229
|
+
|
|
230
|
+
function names_merge_fixable(text: string | undefined): boolean {
|
|
231
|
+
if (text?.includes(FAILURE_MESSAGE_PREFIX) !== true) return false
|
|
232
|
+
|
|
233
|
+
return MERGE_FIXABLE_REASONS.some((reason) => text.includes(reason))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Whether the followup failed on the pull request's checks — a red check or a conflict. A refusal
|
|
237
|
+
// before the wait, or a review blocker alone, is not one: its text carries neither reason.
|
|
238
|
+
function is_checks_failure(failed: JoshResult): boolean {
|
|
239
|
+
return [failed.out, failed.err].some((text) => names_merge_fixable(text))
|
|
240
|
+
}
|
|
241
|
+
|
|
113
242
|
// The result to stop on, or `undefined` once a clean merge is pushed and the followup can wait again.
|
|
243
|
+
// A followup that failed on the checks merges again whenever the default branch moved since the push,
|
|
244
|
+
// conflicting or not: a red CI on the merge ref is then reproduced by the local gate or cleared by the
|
|
245
|
+
// new push. An unmoved default branch, or any other failure, stands.
|
|
114
246
|
async function merge_again(title: string, failed: JoshResult): Promise<StepResult | undefined> {
|
|
115
|
-
if (!(
|
|
247
|
+
if (!is_checks_failure(failed)) return failed
|
|
116
248
|
|
|
117
249
|
const outcome = await merge_outcome()
|
|
118
250
|
|
|
@@ -168,7 +300,16 @@ async function followup_stage(
|
|
|
168
300
|
return await followup_attempt(title, notify, mark, 0)
|
|
169
301
|
}
|
|
170
302
|
|
|
171
|
-
const run_ship_sync = {
|
|
303
|
+
const run_ship_sync = {
|
|
304
|
+
CONFLICT_NOTE,
|
|
305
|
+
MAX_MERGE_RETRIES,
|
|
306
|
+
MERGED_GATE_NOTE,
|
|
307
|
+
PUSHED_NOTE,
|
|
308
|
+
PUSH_FAILED_NOTE,
|
|
309
|
+
catch_up,
|
|
310
|
+
followup_stage,
|
|
311
|
+
sync_stage,
|
|
312
|
+
}
|
|
172
313
|
|
|
173
314
|
export type { StepResult, WaitMark }
|
|
174
315
|
export { run_ship_sync }
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { gate_labels } from '#scripts/gate/gate-labels'
|
|
2
|
+
import type { GateTree } from '#scripts/gate/gate-tree'
|
|
3
|
+
import type { MovedParts } from '#scripts/gate/moved-paths'
|
|
4
|
+
import { scoped_green } from '#scripts/gate/scoped-green'
|
|
5
|
+
import { since_green } from '#scripts/gate/since-green'
|
|
6
|
+
import { changed_file_scope } from '#scripts/git/changed-file-scope'
|
|
7
|
+
import { changed_paths } from '#scripts/git/changed-paths'
|
|
8
|
+
import type { FileMapStampAccess } from '#scripts/josh/file-map-stamp'
|
|
9
|
+
import { run_record_path } from '#scripts/josh/run-record-path'
|
|
10
|
+
import { review_stamps } from '#scripts/review/review-stamps'
|
|
11
|
+
import { test_read_map, type ReadMap } from '#scripts/test/test-read-map'
|
|
12
|
+
import { related_scope } from '#scripts/test/test-related-scope'
|
|
13
|
+
import { test_related_synced, type SyncedSplit } from '#scripts/test/test-related-synced'
|
|
14
|
+
import { whole_suite_files } from '#scripts/test/whole-suite-files'
|
|
15
|
+
|
|
16
|
+
// **A scoped pair whose records went stale only because a merge with the default branch moved the base
|
|
17
|
+
// re-runs nothing when that merge shares nothing with this branch's change** (joshuafolkken/kit#4985).
|
|
18
|
+
// The sync, the commit stage's catch-up and a lane's own conflict merge each moved the base under a
|
|
19
|
+
// pair green on the branch side, and the pair re-ran over the branch's related set — minutes of core
|
|
20
|
+
// budget for main's files, which main's CI had already verified.
|
|
21
|
+
//
|
|
22
|
+
// **Where the two can meet still runs.** A file both sides changed, a test reaching both a synced module
|
|
23
|
+
// and this branch's change (`test-related-synced.ts`), or a synced module the import graph cannot
|
|
24
|
+
// split keeps the pair running as before, narrowed by the checks themselves (`since-green.ts`). So
|
|
25
|
+
// does, for `lint:related`, any file whose imports reach both sides, and for `test:related` a synced
|
|
26
|
+
// file every test depends on or a non-code file a test reads: `josh test:related` leaves those to the
|
|
27
|
+
// branch's own tests, which a settled record would skip. The
|
|
28
|
+
// gate still runs over the merged tree wherever it ran before (joshuafolkken/kit#4825), and CI runs
|
|
29
|
+
// the whole suite.
|
|
30
|
+
//
|
|
31
|
+
// **Whatever cannot be read settles nothing**: a record that is absent or cannot be compared, a branch
|
|
32
|
+
// side that moved since its record, a merge that changed a check's configuration, or a branch change
|
|
33
|
+
// git cannot read.
|
|
34
|
+
|
|
35
|
+
interface SyncedCheck {
|
|
36
|
+
script: string
|
|
37
|
+
label: string
|
|
38
|
+
stamp: FileMapStampAccess
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const CHECKS: ReadonlyArray<SyncedCheck> = [
|
|
42
|
+
{
|
|
43
|
+
script: scoped_green.LINT_SCRIPT,
|
|
44
|
+
label: gate_labels.LINT_LABEL,
|
|
45
|
+
stamp: review_stamps.lint_related_stamp,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
script: scoped_green.TEST_SCRIPT,
|
|
49
|
+
label: gate_labels.UNIT_LABEL,
|
|
50
|
+
stamp: review_stamps.test_related_stamp,
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
function is_sync_only(parts: MovedParts): boolean {
|
|
55
|
+
return (
|
|
56
|
+
parts.synced.length > 0 &&
|
|
57
|
+
parts.changed.every((relative) => run_record_path.is_run_record(relative))
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function synced_of(
|
|
62
|
+
check: SyncedCheck,
|
|
63
|
+
tree: GateTree,
|
|
64
|
+
): Promise<ReadonlyArray<string> | undefined> {
|
|
65
|
+
const parts = await since_green.read_moved_parts(check.stamp, check.label, undefined, tree)
|
|
66
|
+
|
|
67
|
+
return parts !== undefined && is_sync_only(parts) ? parts.synced : undefined
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// What the merge brought in, when every stale record moved by nothing else.
|
|
71
|
+
async function synced_files(
|
|
72
|
+
checks: ReadonlyArray<SyncedCheck>,
|
|
73
|
+
tree: GateTree,
|
|
74
|
+
): Promise<ReadonlyArray<string> | undefined> {
|
|
75
|
+
const parts = await Promise.all(checks.map(async (check) => await synced_of(check, tree)))
|
|
76
|
+
const known = parts.filter((synced) => synced !== undefined)
|
|
77
|
+
|
|
78
|
+
return known.length > 0 && known.length === parts.length ? [...new Set(known.flat())] : undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function branch_change(): Promise<ReadonlyArray<string> | undefined> {
|
|
82
|
+
try {
|
|
83
|
+
return await changed_paths.read_changed_paths(false)
|
|
84
|
+
} catch {
|
|
85
|
+
return undefined
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function is_whole_suite(file: string): boolean {
|
|
90
|
+
return whole_suite_files.WHOLE_SUITE_FILES.has(file)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// A synced file a test can reach with no import the graph splits: one every test depends on, or a
|
|
94
|
+
// non-code file a recorded test reads — every one of them when no map was recorded.
|
|
95
|
+
function is_read_by_tests(map: ReadMap | undefined, file: string): boolean {
|
|
96
|
+
if (is_whole_suite(file)) return true
|
|
97
|
+
if (related_scope.is_relatable(file)) return false
|
|
98
|
+
|
|
99
|
+
return map === undefined || test_read_map.readers_of(map, file).length > 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function has_check(checks: ReadonlyArray<SyncedCheck>, script: string): boolean {
|
|
103
|
+
return checks.some((check) => check.script === script)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// What only one check reads where the two sides meet. `lint:related`'s type-aware rules read every
|
|
107
|
+
// file whose imports reach both, code between included; `test:related` leaves a file the graph
|
|
108
|
+
// cannot split to the branch's own tests (`test-related.ts`), which a settled record would skip.
|
|
109
|
+
function check_overlap(
|
|
110
|
+
checks: ReadonlyArray<SyncedCheck>,
|
|
111
|
+
synced: ReadonlyArray<string>,
|
|
112
|
+
split: SyncedSplit,
|
|
113
|
+
map: ReadMap | undefined,
|
|
114
|
+
): ReadonlyArray<string> {
|
|
115
|
+
const meeting = has_check(checks, scoped_green.LINT_SCRIPT) ? split.meeting : []
|
|
116
|
+
const read = has_check(checks, scoped_green.TEST_SCRIPT)
|
|
117
|
+
? synced.filter((file) => is_read_by_tests(map, file))
|
|
118
|
+
: []
|
|
119
|
+
|
|
120
|
+
return [...meeting, ...read]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The synced files and tests where the merge and this branch's change meet.
|
|
124
|
+
async function overlap_of(
|
|
125
|
+
checks: ReadonlyArray<SyncedCheck>,
|
|
126
|
+
synced: ReadonlyArray<string>,
|
|
127
|
+
branch: ReadonlyArray<string>,
|
|
128
|
+
): Promise<ReadonlyArray<string>> {
|
|
129
|
+
const root = await changed_file_scope.repository_root_or_cwd()
|
|
130
|
+
const map = test_read_map.read_map(root)
|
|
131
|
+
const split = await test_related_synced.split(root, synced, branch, map)
|
|
132
|
+
const both = synced.filter((file) => branch.includes(file))
|
|
133
|
+
// Code the graph left unsplit — whole-suite modules included, which it never splits.
|
|
134
|
+
const unsplit = synced.filter(
|
|
135
|
+
(file) =>
|
|
136
|
+
related_scope.is_relatable(file) && !branch.includes(file) && !split.left.includes(file),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return [...both, ...split.shared, ...unsplit, ...check_overlap(checks, synced, split, map)]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function unshared_sync(
|
|
143
|
+
checks: ReadonlyArray<SyncedCheck>,
|
|
144
|
+
tree: GateTree,
|
|
145
|
+
): Promise<ReadonlyArray<string> | undefined> {
|
|
146
|
+
const synced = await synced_files(checks, tree)
|
|
147
|
+
const branch = synced === undefined ? undefined : await branch_change()
|
|
148
|
+
|
|
149
|
+
if (synced === undefined || branch === undefined) return undefined
|
|
150
|
+
|
|
151
|
+
const overlap = await overlap_of(checks, synced, branch)
|
|
152
|
+
|
|
153
|
+
return overlap.length === 0 ? synced : undefined
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function reason(scripts: ReadonlyArray<string>, synced: ReadonlyArray<string>): string {
|
|
157
|
+
return (
|
|
158
|
+
`scoped pair: ${scripts.join(' and ')} not re-run — the ${String(synced.length)} file(s) ` +
|
|
159
|
+
"the merge with the default branch brought in share no file or test with this branch's change."
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// The line a settled pair prints, after re-recording each stale check green on this tree so the gate
|
|
164
|
+
// and the brief read it as checked — or `undefined`, and the pair runs as before.
|
|
165
|
+
async function settle(tree: GateTree, scripts: ReadonlyArray<string>): Promise<string | undefined> {
|
|
166
|
+
const checks = CHECKS.filter((check) => scripts.includes(check.script))
|
|
167
|
+
const synced = await unshared_sync(checks, tree)
|
|
168
|
+
|
|
169
|
+
if (synced === undefined) return undefined
|
|
170
|
+
|
|
171
|
+
for (const check of checks) scoped_green.record_green(check.stamp, tree)
|
|
172
|
+
|
|
173
|
+
return reason(scripts, synced)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const run_ship_synced_scope = { settle }
|
|
177
|
+
|
|
178
|
+
export { run_ship_synced_scope }
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { setTimeout as sleep } from 'node:timers/promises'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { issue_number_shape } from '#scripts/issue/issue-number-shape'
|
|
5
|
+
import { run_ship_detach } from './run-ship-detach'
|
|
6
|
+
import { run_ship_probe } from './run-ship-probe'
|
|
7
|
+
import { run_ship_stage } from './run-ship-stage'
|
|
8
|
+
import { run_ship_waiter } from './run-ship-waiter'
|
|
9
|
+
|
|
10
|
+
// `josh ship:wait <N>` — the lane session that detached a ship waits here for the gate and review
|
|
11
|
+
// round 1, so a stop there comes back to it (`run-ship-waiter.ts`) rather than to a fresh session
|
|
12
|
+
// (joshuafolkken/kit#4711). One verdict line, then the stop note on `returned`:
|
|
13
|
+
//
|
|
14
|
+
// - `returned` — the supervisor stopped and handed its note to this session: fix it and ship again.
|
|
15
|
+
// - `passed` — the ship committed, or finished: what follows is the supervisor's alone.
|
|
16
|
+
// - `ended` — the supervisor ended with no note for this session: nothing is left to wait for.
|
|
17
|
+
// - `pending` — the chunk ran out with the ship still before its commit: issue the wait again.
|
|
18
|
+
//
|
|
19
|
+
// **A chunk ends below the five-minute prompt-cache TTL a lane child runs on** (`lane-child-marker.ts`),
|
|
20
|
+
// so re-issuing it is the keep-alive: the next request reads the cached context back (66k tokens on
|
|
21
|
+
// Opus 5.5, about $0.013) instead of writing it whole again after the wait (about $0.33) —
|
|
22
|
+
// joshuafolkken/kit#5344. The heartbeat outlives the chunk by the session gap.
|
|
23
|
+
|
|
24
|
+
const VERDICT = {
|
|
25
|
+
RETURNED: 'returned',
|
|
26
|
+
PASSED: 'passed',
|
|
27
|
+
ENDED: 'ended',
|
|
28
|
+
PENDING: 'pending',
|
|
29
|
+
} as const
|
|
30
|
+
|
|
31
|
+
type Verdict = (typeof VERDICT)[keyof typeof VERDICT]
|
|
32
|
+
|
|
33
|
+
interface Waited {
|
|
34
|
+
verdict: Verdict
|
|
35
|
+
note?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const CHUNK_MS = 270_000
|
|
39
|
+
const POLL_MS = 2000
|
|
40
|
+
const ARGV_OFFSET = 2
|
|
41
|
+
const SUCCESS_EXIT_CODE = 0
|
|
42
|
+
const FAILURE_EXIT_CODE = 1
|
|
43
|
+
const USAGE = 'Usage: josh ship:wait <N>'
|
|
44
|
+
const NO_REPOSITORY_NOTE = 'not inside a git repository'
|
|
45
|
+
|
|
46
|
+
const { COMMIT, ROUND_TWO, FOLLOWUP, REPORT } = run_ship_stage.STAGE
|
|
47
|
+
const PAST_COMMIT: ReadonlySet<string> = new Set([ROUND_TWO, FOLLOWUP, REPORT])
|
|
48
|
+
|
|
49
|
+
// Past the commit in the current attempt: the record's `done` outlives a stop after the commit, so a
|
|
50
|
+
// re-ship after a followup stop would read as committed before its own gate and review ran. `started`
|
|
51
|
+
// is cleared at every stop, so it says what this attempt reached.
|
|
52
|
+
function is_committed(repository: string, issue: string): boolean {
|
|
53
|
+
const target = run_ship_stage.record_path(repository, issue)
|
|
54
|
+
const started = run_ship_stage.read_started(target)
|
|
55
|
+
|
|
56
|
+
if (started.some((stage) => PAST_COMMIT.has(stage))) return true
|
|
57
|
+
|
|
58
|
+
return started.includes(COMMIT) && run_ship_stage.read_done(target).has(COMMIT)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The supervisor's own end, where it left no note: `undefined` while it still runs.
|
|
62
|
+
function ended(repository: string, issue: string): Waited | undefined {
|
|
63
|
+
const result = run_ship_detach.read_result(repository, issue)?.result
|
|
64
|
+
|
|
65
|
+
if (result === 'running') return undefined
|
|
66
|
+
|
|
67
|
+
return { verdict: result === 'success' ? VERDICT.PASSED : VERDICT.ENDED }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function settled(repository: string, issue: string): Waited | undefined {
|
|
71
|
+
const note = run_ship_waiter.take(repository, issue)
|
|
72
|
+
|
|
73
|
+
if (note !== undefined) return { verdict: VERDICT.RETURNED, note }
|
|
74
|
+
if (is_committed(repository, issue)) return { verdict: VERDICT.PASSED }
|
|
75
|
+
|
|
76
|
+
return ended(repository, issue)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function poll(repository: string, issue: string, deadline: number): Promise<Waited> {
|
|
80
|
+
while (Date.now() < deadline) {
|
|
81
|
+
run_ship_waiter.beat(repository, issue)
|
|
82
|
+
const waited = settled(repository, issue)
|
|
83
|
+
|
|
84
|
+
if (waited !== undefined) return waited
|
|
85
|
+
|
|
86
|
+
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the supervisor's progress
|
|
87
|
+
await sleep(POLL_MS)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { verdict: VERDICT.PENDING }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// A `pending` chunk keeps the heartbeat for the next one; any other verdict ends the wait for good.
|
|
94
|
+
async function wait_for(repository: string, issue: string, chunk_ms = CHUNK_MS): Promise<Waited> {
|
|
95
|
+
const waited = await poll(repository, issue, Date.now() + chunk_ms)
|
|
96
|
+
|
|
97
|
+
if (waited.verdict !== VERDICT.PENDING) run_ship_waiter.leave(repository, issue)
|
|
98
|
+
|
|
99
|
+
return waited
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Each verdict carries what the session does next, so the instruction lives with the command.
|
|
103
|
+
function next_action(issue: string): Record<Verdict, string> {
|
|
104
|
+
return {
|
|
105
|
+
[VERDICT.RETURNED]: 'the ship stopped: fix what the note below says, then ship again',
|
|
106
|
+
[VERDICT.PASSED]: 'the supervisor ships the rest: end the turn',
|
|
107
|
+
[VERDICT.ENDED]: 'nothing is left to wait for: end the turn',
|
|
108
|
+
[VERDICT.PENDING]: `still before the commit: on \`pnpm josh cost --cut\` \`under\`, run \`pnpm josh ship:wait ${issue}\` again; else end the turn`,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function report(issue: string, waited: Waited): void {
|
|
113
|
+
console.info(`${waited.verdict} — ${next_action(issue)[waited.verdict]}`)
|
|
114
|
+
if (waited.note !== undefined) console.info(waited.note)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
118
|
+
const issue = issue_number_shape.single_of(argv)
|
|
119
|
+
|
|
120
|
+
if (issue === undefined) {
|
|
121
|
+
console.error(USAGE)
|
|
122
|
+
|
|
123
|
+
return FAILURE_EXIT_CODE
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const repository = await run_ship_probe.repository_directory()
|
|
127
|
+
|
|
128
|
+
if (repository === undefined) {
|
|
129
|
+
console.error(NO_REPOSITORY_NOTE)
|
|
130
|
+
|
|
131
|
+
return FAILURE_EXIT_CODE
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
report(issue, await wait_for(repository, issue))
|
|
135
|
+
|
|
136
|
+
return SUCCESS_EXIT_CODE
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const run_ship_wait_cli = { CHUNK_MS, VERDICT, run, wait_for }
|
|
140
|
+
|
|
141
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
142
|
+
process.exitCode = await run(process.argv.slice(ARGV_OFFSET))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export { run_ship_wait_cli }
|