@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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { git_command } from '#scripts/git/git-command'
|
|
2
|
+
import { session_cite } from '#scripts/issue/session-cite'
|
|
3
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
|
+
import { run_board_closed, type ClosedIssue } from '#scripts/run/board/run-board-closed'
|
|
5
|
+
import { run_event_scope } from '#scripts/run/event/run-event-scope'
|
|
6
|
+
import type { RunEvent } from '#scripts/run/event/run-event-stream'
|
|
7
|
+
import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
|
|
8
|
+
import { lane_registry, type LaneInfo } from './lane-registry'
|
|
9
|
+
import { lane_withdraw, type Withdrawer } from './lane-withdraw'
|
|
10
|
+
|
|
11
|
+
// Every open lane whose issue was closed on GitHub as not planned is withdrawn through `lane:withdraw`'s
|
|
12
|
+
// own path (joshuafolkken/kit#4915): its child is stopped, its work stashed and the lane closed, so the
|
|
13
|
+
// lane leaves the count and no cut resumes it. Nothing else ends such a lane: the child keeps working on
|
|
14
|
+
// an issue no one wants, and the lane limit counts it while the board draws no lane for it.
|
|
15
|
+
//
|
|
16
|
+
// **Only a not-planned close is swept.** A merged issue's lane is ended by the merge path, where a
|
|
17
|
+
// detached ship may still be running its tail; an issue closed as completed by hand is left to
|
|
18
|
+
// `lane:prune` and `run:merge`, which books it withdrawn when no merged pull request closed it
|
|
19
|
+
// (joshuafolkken/kit#4916). An issue that could not be read is
|
|
20
|
+
// asked again on the next pass, and a lane whose withdrawal is already on the event stream is not
|
|
21
|
+
// withdrawn twice.
|
|
22
|
+
//
|
|
23
|
+
// **A branch with commits on no remote is kept**: the close deletes the branch and the stash keeps only
|
|
24
|
+
// uncommitted work, so a person decides with `lane:withdraw` by hand. `run:merge` withdraws through the
|
|
25
|
+
// same guard (`withdraw_lane`).
|
|
26
|
+
|
|
27
|
+
const SWEEP_REASON = 'its issue was closed as not planned while the lane was still open'
|
|
28
|
+
|
|
29
|
+
function is_not_planned(read: ClosedIssue | undefined): boolean {
|
|
30
|
+
return read !== undefined && read.is_not_planned && !read.is_merged
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The open lanes whose issue is closed as not planned. */
|
|
34
|
+
async function closed_lanes(): Promise<Array<LaneInfo>> {
|
|
35
|
+
const listed = await lane_registry.list_lanes()
|
|
36
|
+
const lanes = listed.filter((lane) => !lane.is_stranded)
|
|
37
|
+
const { closed } = await run_board_closed.read_all(lanes.map((lane) => Number(lane.issue)))
|
|
38
|
+
|
|
39
|
+
return lanes.filter((lane) => is_not_planned(closed.get(Number(lane.issue))))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// An unreadable count keeps the branch, as unpushed commits would.
|
|
43
|
+
async function has_unpushed(branch: string): Promise<boolean> {
|
|
44
|
+
try {
|
|
45
|
+
return (await git_command.commit_count_unpushed(branch)) > 0
|
|
46
|
+
} catch (error) {
|
|
47
|
+
error_text.trace_swallowed('lane_withdraw_closed.has_unpushed', error)
|
|
48
|
+
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function kept_note(issue: string, by: Withdrawer): string {
|
|
54
|
+
const settled = by === 'yield' ? 'gave its lane back' : 'is closed without a merge'
|
|
55
|
+
|
|
56
|
+
return `${session_cite.issue(issue)} ${settled}, but its branch has commits on no remote, so its lane is kept — run \`pnpm josh lane:withdraw ${issue}\` by hand.`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface UnattendedWithdrawal {
|
|
60
|
+
note: string
|
|
61
|
+
// The withdrawal could not read the issue, so nothing was stopped or closed.
|
|
62
|
+
is_retry: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Withdraw a closed issue's open lane with no person present, keeping a branch with unpushed commits. */
|
|
66
|
+
async function withdraw_lane(
|
|
67
|
+
lane: LaneInfo,
|
|
68
|
+
reason: string,
|
|
69
|
+
by: Withdrawer,
|
|
70
|
+
): Promise<UnattendedWithdrawal> {
|
|
71
|
+
if (await has_unpushed(lane.branch)) return { note: kept_note(lane.issue, by), is_retry: false }
|
|
72
|
+
const outcome = await lane_withdraw.withdraw(lane.issue, reason, by)
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
note: lane_withdraw.describe(outcome, lane.issue),
|
|
76
|
+
is_retry: outcome.kind === 'unreadable',
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function sweep_lane(
|
|
81
|
+
lane: LaneInfo,
|
|
82
|
+
events: ReadonlyArray<RunEvent>,
|
|
83
|
+
): Promise<string | undefined> {
|
|
84
|
+
if (run_event_scope.is_withdrawn(events, lane.issue)) return undefined
|
|
85
|
+
const { note } = await withdraw_lane(lane, SWEEP_REASON, 'supervisor')
|
|
86
|
+
|
|
87
|
+
return note
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// One lane's failure does not stop the sweep: the rest are still withdrawn, and this one is asked again.
|
|
91
|
+
async function try_sweep_lane(
|
|
92
|
+
lane: LaneInfo,
|
|
93
|
+
events: ReadonlyArray<RunEvent>,
|
|
94
|
+
): Promise<string | undefined> {
|
|
95
|
+
try {
|
|
96
|
+
return await sweep_lane(lane, events)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
error_text.trace_swallowed('lane_withdraw_closed.sweep_lane', error)
|
|
99
|
+
|
|
100
|
+
return `${session_cite.issue(lane.issue)} could not be withdrawn (${error_text.message_of(error)}); the next pass tries again.`
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Withdraw every open lane whose issue is closed as not planned — the supervisor's pass; one note each. */
|
|
105
|
+
async function withdraw_closed(): Promise<Array<string>> {
|
|
106
|
+
const lanes = await closed_lanes()
|
|
107
|
+
const events = await run_event_stream_emit.current_events()
|
|
108
|
+
const notes: Array<string> = []
|
|
109
|
+
|
|
110
|
+
// Sequential, as `lane_close.close_each` is: two worktree removals at once contend for git's
|
|
111
|
+
// registration directory.
|
|
112
|
+
for (const lane of lanes) {
|
|
113
|
+
// eslint-disable-next-line no-await-in-loop -- worktree writes contend for the same registration directory
|
|
114
|
+
const note = await try_sweep_lane(lane, events)
|
|
115
|
+
|
|
116
|
+
if (note !== undefined) notes.push(note)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return notes
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const lane_withdraw_closed = { SWEEP_REASON, withdraw_closed, withdraw_lane }
|
|
123
|
+
|
|
124
|
+
export { lane_withdraw_closed }
|
|
@@ -12,6 +12,7 @@ import { lane_reap } from './lane-reap'
|
|
|
12
12
|
import { lane_registry, type LaneInfo } from './lane-registry'
|
|
13
13
|
import { lane_residents } from './lane-residents'
|
|
14
14
|
import { lane_state } from './lane-state'
|
|
15
|
+
import { lane_yield } from './lane-yield'
|
|
15
16
|
|
|
16
17
|
// `josh lane:withdraw <N> --reason <text>` — a person takes a running lane child's issue out of the
|
|
17
18
|
// current run: the child is stopped, its work stashed and the lane closed. **The Issue stays open** —
|
|
@@ -25,9 +26,15 @@ import { lane_state } from './lane-state'
|
|
|
25
26
|
//
|
|
26
27
|
// A tree that could not be stashed is left in place: closing the lane would delete the work, and the
|
|
27
28
|
// person decides what to keep.
|
|
29
|
+
//
|
|
30
|
+
// **An issue closed on GitHub while its lane stayed open is withdrawn the same way**
|
|
31
|
+
// (joshuafolkken/kit#4915): its child would otherwise keep working, be resumed by the next cut and
|
|
32
|
+
// hold a lane. `lane-withdraw-closed.ts` sweeps those lanes each supervisor pass, and the event, the
|
|
33
|
+
// stash note and the issue comment name the supervisor rather than a person. `run:merge` withdraws a
|
|
34
|
+
// returned child whose issue closed without a merge the same way, named as itself
|
|
35
|
+
// (`run-merge-withdrawn.ts`, joshuafolkken/kit#4916).
|
|
28
36
|
|
|
29
37
|
const WITHDRAW_OCCASION = 'lane withdraw'
|
|
30
|
-
const WITHDRAW_CAUSE = 'a person withdrew it with `lane:withdraw`'
|
|
31
38
|
const CLOSED_STATE = 'CLOSED'
|
|
32
39
|
const CLOSED_KIND = 'closed'
|
|
33
40
|
const NO_LANE_KIND = 'no-lane'
|
|
@@ -35,13 +42,63 @@ const UNREADABLE_KIND = 'unreadable'
|
|
|
35
42
|
|
|
36
43
|
type Unsaved = Extract<KeptWork, { kind: 'unsaved' }>
|
|
37
44
|
|
|
45
|
+
// Who withdrew the lane: a person with `lane:withdraw`, the supervisor's sweep of closed issues,
|
|
46
|
+
// `run:merge` collecting a child whose issue closed without a merge (joshuafolkken/kit#4916), or the
|
|
47
|
+
// child's own `lane:plan` giving its lane back (`lane-yield.ts`, joshuafolkken/kit#4926).
|
|
48
|
+
type Withdrawer = 'person' | 'supervisor' | 'merge' | 'yield'
|
|
49
|
+
|
|
50
|
+
const OPEN_TAIL =
|
|
51
|
+
'Its lane child was stopped and the lane closed. The Issue stays open: this run does not resume it, and the next run offers it again.'
|
|
52
|
+
// What `describe` says of an open issue's place in the run once its lane is withdrawn.
|
|
53
|
+
const STAYS_OPEN = 'The Issue stays open: this run does not offer it again, the next run does.'
|
|
54
|
+
|
|
55
|
+
// How each withdrawer is named on the event stream, in the stash note and on the issue.
|
|
56
|
+
const WITHDRAWER_TEXT = {
|
|
57
|
+
person: {
|
|
58
|
+
name: 'a person',
|
|
59
|
+
cause: 'a person withdrew it with `lane:withdraw`',
|
|
60
|
+
closed_head:
|
|
61
|
+
'Its lane was withdrawn with `pnpm josh lane:withdraw` after the Issue was closed.',
|
|
62
|
+
open_head: 'Withdrawn from the current run by a person with `pnpm josh lane:withdraw`.',
|
|
63
|
+
open_tail: OPEN_TAIL,
|
|
64
|
+
stays: STAYS_OPEN,
|
|
65
|
+
},
|
|
66
|
+
supervisor: {
|
|
67
|
+
name: 'the run:wake supervisor',
|
|
68
|
+
cause: 'the `run:wake` supervisor withdrew it after its issue was closed',
|
|
69
|
+
closed_head: 'Its lane was withdrawn by the `run:wake` supervisor after the Issue was closed.',
|
|
70
|
+
open_head: 'Withdrawn from the current run by the `run:wake` supervisor.',
|
|
71
|
+
open_tail: OPEN_TAIL,
|
|
72
|
+
stays: STAYS_OPEN,
|
|
73
|
+
},
|
|
74
|
+
merge: {
|
|
75
|
+
name: 'the run:merge driver',
|
|
76
|
+
cause: '`run:merge` withdrew it after its issue was closed without a merge',
|
|
77
|
+
closed_head:
|
|
78
|
+
'Its lane was withdrawn by `pnpm josh run:merge` after the Issue was closed without a merge.',
|
|
79
|
+
open_head: 'Withdrawn from the current run by `pnpm josh run:merge`.',
|
|
80
|
+
open_tail: OPEN_TAIL,
|
|
81
|
+
stays: STAYS_OPEN,
|
|
82
|
+
},
|
|
83
|
+
yield: lane_yield.YIELD_TEXT,
|
|
84
|
+
} as const satisfies Record<Withdrawer, Record<string, string>>
|
|
85
|
+
|
|
86
|
+
interface Withdrawal {
|
|
87
|
+
reason: string
|
|
88
|
+
by: Withdrawer
|
|
89
|
+
is_closed: boolean
|
|
90
|
+
}
|
|
91
|
+
|
|
38
92
|
interface Withdrawn {
|
|
39
93
|
kind: 'withdrawn'
|
|
94
|
+
by: Withdrawer
|
|
40
95
|
// How many of the child's processes the withdrawal stopped — 0 for a child that had already ended.
|
|
41
96
|
reaped: number
|
|
42
97
|
stash: string | undefined
|
|
43
|
-
// Whether the comment saying the issue
|
|
98
|
+
// Whether the comment saying so reached the issue.
|
|
44
99
|
is_noted: boolean
|
|
100
|
+
// The issue was already closed — withdrawn or finished on GitHub while its lane stayed open.
|
|
101
|
+
is_closed: boolean
|
|
45
102
|
lane: CloseKind
|
|
46
103
|
// What else the close ended from the checkout; `undefined` where it could not be read.
|
|
47
104
|
residents: ReadonlyArray<number> | undefined
|
|
@@ -51,51 +108,54 @@ type Refused =
|
|
|
51
108
|
{ kind: typeof CLOSED_KIND } | { kind: typeof NO_LANE_KIND } | { kind: typeof UNREADABLE_KIND }
|
|
52
109
|
type WithdrawOutcome = Withdrawn | Unsaved | Refused
|
|
53
110
|
|
|
54
|
-
function withdraw_comment(reason:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
'Its lane child was stopped and the lane closed
|
|
58
|
-
|
|
59
|
-
|
|
111
|
+
function withdraw_comment({ reason, by, is_closed }: Withdrawal): string {
|
|
112
|
+
const head = is_closed ? WITHDRAWER_TEXT[by].closed_head : WITHDRAWER_TEXT[by].open_head
|
|
113
|
+
const tail = is_closed
|
|
114
|
+
? 'Its lane child was stopped and the lane closed, so it no longer holds a lane and is not resumed.'
|
|
115
|
+
: WITHDRAWER_TEXT[by].open_tail
|
|
116
|
+
|
|
117
|
+
return [head, tail, `- Why: ${reason}`].join('\n')
|
|
60
118
|
}
|
|
61
119
|
|
|
62
|
-
async function record_withdraw(issue: string, reason:
|
|
120
|
+
async function record_withdraw(issue: string, { reason, by }: Withdrawal): Promise<void> {
|
|
63
121
|
await run_event_stream_emit.emit(
|
|
64
122
|
run_event_stream.EVENT_KIND.WITHDRAW,
|
|
65
|
-
`${issue_cite.plain(issue)} withdrawn by
|
|
123
|
+
`${issue_cite.plain(issue)} withdrawn by ${WITHDRAWER_TEXT[by].name}: ${reason}`,
|
|
66
124
|
)
|
|
67
125
|
}
|
|
68
126
|
|
|
69
|
-
//
|
|
70
|
-
async function
|
|
127
|
+
// Whether the issue is closed, or `undefined` when it could not be read — an unread issue writes nothing.
|
|
128
|
+
async function read_is_closed(issue: string): Promise<boolean | undefined> {
|
|
71
129
|
const read = await issue_state_cli.read_issue(issue)
|
|
72
130
|
|
|
73
|
-
if (read.kind !== 'state') return
|
|
74
|
-
if (read.state.state.toUpperCase() === CLOSED_STATE) return { kind: CLOSED_KIND }
|
|
131
|
+
if (read.kind !== 'state') return undefined
|
|
75
132
|
|
|
76
|
-
return
|
|
133
|
+
return read.state.state.toUpperCase() === CLOSED_STATE
|
|
77
134
|
}
|
|
78
135
|
|
|
79
136
|
async function note_issue(
|
|
80
137
|
issue: string,
|
|
81
|
-
|
|
138
|
+
withdrawal: Withdrawal,
|
|
82
139
|
stash: string | undefined,
|
|
83
140
|
): Promise<boolean> {
|
|
84
|
-
const is_noted = await git_gh_issue_write.issue_try_comment(issue, withdraw_comment(
|
|
141
|
+
const is_noted = await git_gh_issue_write.issue_try_comment(issue, withdraw_comment(withdrawal))
|
|
142
|
+
|
|
143
|
+
if (stash !== undefined) {
|
|
144
|
+
await lane_preserve.announce(issue, stash, WITHDRAWER_TEXT[withdrawal.by].cause)
|
|
145
|
+
}
|
|
85
146
|
|
|
86
|
-
if (stash !== undefined) await lane_preserve.announce(issue, stash, WITHDRAW_CAUSE)
|
|
87
147
|
await run_label.unmark(issue)
|
|
88
148
|
|
|
89
149
|
return is_noted
|
|
90
150
|
}
|
|
91
151
|
|
|
92
|
-
async function stop_and_close(lane: LaneInfo,
|
|
93
|
-
await record_withdraw(lane.issue,
|
|
152
|
+
async function stop_and_close(lane: LaneInfo, withdrawal: Withdrawal): Promise<WithdrawOutcome> {
|
|
153
|
+
await record_withdraw(lane.issue, withdrawal)
|
|
94
154
|
const reaped = lane_reap.reap_child(lane).length
|
|
95
155
|
const saved = await lane_preserve.keep_work(lane.issue, lane.directory, WITHDRAW_OCCASION)
|
|
96
156
|
|
|
97
157
|
if (saved.kind === 'unsaved') return saved
|
|
98
|
-
const is_noted = await note_issue(lane.issue,
|
|
158
|
+
const is_noted = await note_issue(lane.issue, withdrawal, saved.stash)
|
|
99
159
|
const closed = await lane_close.close_lane(lane.issue)
|
|
100
160
|
|
|
101
161
|
// After the close, so the record names the withdrawal rather than the close it ran. An `incomplete`
|
|
@@ -106,25 +166,34 @@ async function stop_and_close(lane: LaneInfo, reason: string): Promise<WithdrawO
|
|
|
106
166
|
|
|
107
167
|
return {
|
|
108
168
|
kind: 'withdrawn',
|
|
169
|
+
by: withdrawal.by,
|
|
109
170
|
reaped,
|
|
110
171
|
stash: saved.stash,
|
|
111
172
|
is_noted,
|
|
173
|
+
is_closed: withdrawal.is_closed,
|
|
112
174
|
lane: closed.kind,
|
|
113
175
|
residents: closed.residents,
|
|
114
176
|
}
|
|
115
177
|
}
|
|
116
178
|
|
|
117
|
-
/**
|
|
118
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Stop `#<N>`'s lane child, stash its work and close the lane, leaving the Issue as it is. A closed
|
|
181
|
+
* issue is refused only when no lane is left for it (joshuafolkken/kit#4915).
|
|
182
|
+
*/
|
|
183
|
+
async function withdraw(
|
|
184
|
+
issue: string,
|
|
185
|
+
reason: string,
|
|
186
|
+
by: Withdrawer = 'person',
|
|
187
|
+
): Promise<WithdrawOutcome> {
|
|
119
188
|
issue_number_shape.require_issue_number(issue)
|
|
120
|
-
const
|
|
189
|
+
const is_closed = await read_is_closed(issue)
|
|
121
190
|
|
|
122
|
-
if (
|
|
191
|
+
if (is_closed === undefined) return { kind: UNREADABLE_KIND }
|
|
123
192
|
const lane = await lane_registry.find_open_lane(issue)
|
|
124
193
|
|
|
125
|
-
if (lane === undefined) return { kind: NO_LANE_KIND }
|
|
194
|
+
if (lane === undefined) return { kind: is_closed ? CLOSED_KIND : NO_LANE_KIND }
|
|
126
195
|
|
|
127
|
-
return await stop_and_close(lane, reason)
|
|
196
|
+
return await stop_and_close(lane, { reason, by, is_closed })
|
|
128
197
|
}
|
|
129
198
|
|
|
130
199
|
function stash_sentence(stash: string | undefined): string {
|
|
@@ -147,8 +216,11 @@ function withdrawn_text(outcome: Withdrawn, issue: string): string {
|
|
|
147
216
|
const head = `Withdrew ${cited}. ${stopped} ${stash_sentence(outcome.stash)}`
|
|
148
217
|
const lane = lane_suffix(outcome, cited)
|
|
149
218
|
const noted = outcome.is_noted ? '' : ' The comment saying so could not be posted.'
|
|
219
|
+
const issue_sentence = outcome.is_closed
|
|
220
|
+
? 'The Issue was already closed, so its lane no longer holds a place.'
|
|
221
|
+
: WITHDRAWER_TEXT[outcome.by].stays
|
|
150
222
|
|
|
151
|
-
return `${head}
|
|
223
|
+
return `${head} ${issue_sentence}${noted}${lane}`
|
|
152
224
|
}
|
|
153
225
|
|
|
154
226
|
function unsaved_text(outcome: Unsaved, issue: string): string {
|
|
@@ -159,7 +231,7 @@ function refused_text(outcome: Refused, issue: string): string {
|
|
|
159
231
|
const cited = session_cite.issue(issue)
|
|
160
232
|
|
|
161
233
|
if (outcome.kind === CLOSED_KIND) {
|
|
162
|
-
return `${cited} is already closed, so there is nothing to withdraw.`
|
|
234
|
+
return `${cited} is already closed and no lane is open for it, so there is nothing to withdraw.`
|
|
163
235
|
}
|
|
164
236
|
|
|
165
237
|
if (outcome.kind === NO_LANE_KIND) {
|
|
@@ -177,7 +249,12 @@ function describe(outcome: WithdrawOutcome, issue: string): string {
|
|
|
177
249
|
: refused_text(outcome, issue)
|
|
178
250
|
}
|
|
179
251
|
|
|
180
|
-
|
|
252
|
+
// How every withdrawer is named in the `withdraw` event — the board reads the reason after it.
|
|
253
|
+
const WITHDRAWER_NAMES: ReadonlyArray<string> = Object.values(WITHDRAWER_TEXT).map(
|
|
254
|
+
(text) => text.name,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
const lane_withdraw = { WITHDRAWER_NAMES, describe, withdraw }
|
|
181
258
|
|
|
182
|
-
export type { WithdrawOutcome }
|
|
259
|
+
export type { Withdrawer, WithdrawOutcome }
|
|
183
260
|
export { lane_withdraw }
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { issue_cite } from '#scripts/issue/issue-cite'
|
|
2
|
+
import { run_event_scope } from '#scripts/run/event/run-event-scope'
|
|
3
|
+
import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
|
|
4
|
+
import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
|
|
5
|
+
|
|
6
|
+
// A lane child that gives its lane back (joshuafolkken/kit#4926): `lane:plan` found a running lane that
|
|
7
|
+
// planned one of its files first, so instead of polling for the holder to end while its seat stays taken,
|
|
8
|
+
// it notes its yield on the stream and ends. `run:merge` books it `yielded` — neither a merge, a failure
|
|
9
|
+
// nor an outage — and closes the lane through `lane:withdraw`'s path, named as this withdrawer.
|
|
10
|
+
//
|
|
11
|
+
// **The yield is a `note`, not a `withdraw`**: a `withdraw` event says the lane is already closed, and
|
|
12
|
+
// the readers of the stream (`backlog-drive-restore.ts`, the `run:wake` sweep, `run:tidy`) act on that.
|
|
13
|
+
// The yielding child cannot close its own lane, so its issue stays in flight until `run:merge` does.
|
|
14
|
+
//
|
|
15
|
+
// **Unlike a person's withdrawal, a yield does not take the issue out of the run.** Its plan outlives the
|
|
16
|
+
// lane (`lane-plan.ts` → `release`), so `backlog:next` holds it back on that plan while the holder runs
|
|
17
|
+
// and offers it again once the holder's lane ends; `backlog-drive-restore.ts` reads `is_yield` to leave
|
|
18
|
+
// the closing withdrawal out of the withdrawn and the settled issues.
|
|
19
|
+
|
|
20
|
+
// The reason `run:merge` closes a yielded lane with.
|
|
21
|
+
const YIELD_REASON = 'it gave its lane back to an earlier lane holding one of its planned files'
|
|
22
|
+
|
|
23
|
+
const YIELD_MARK = ' yielded its lane: '
|
|
24
|
+
|
|
25
|
+
const YIELD_TEXT = {
|
|
26
|
+
name: 'its own lane:plan',
|
|
27
|
+
cause: '`lane:plan` gave its lane back to an earlier lane holding one of its planned files',
|
|
28
|
+
closed_head: 'Its lane was given back by `pnpm josh lane:plan` after the Issue was closed.',
|
|
29
|
+
open_head:
|
|
30
|
+
'Gave its lane back: `pnpm josh lane:plan` found an earlier lane holding one of its planned files.',
|
|
31
|
+
open_tail:
|
|
32
|
+
'The lane closed before any implementation edit. The Issue stays in the run: it is offered again once the holding lane ends, keeping its place in line.',
|
|
33
|
+
stays: 'The Issue stays in the run: it is offered again once the holding lane ends.',
|
|
34
|
+
} as const
|
|
35
|
+
|
|
36
|
+
// Where in the lane the yield fell, one word on the note, kept for a later count of how many the entry
|
|
37
|
+
// probe caught before the investigation (joshuafolkken/kit#5211): `entry` from `run:entry`, `summary` from the
|
|
38
|
+
// `lane:plan` a child runs once its work summary is set.
|
|
39
|
+
type YieldAt = 'entry' | 'summary'
|
|
40
|
+
|
|
41
|
+
const YIELD_AT_MARK = ' · at: '
|
|
42
|
+
|
|
43
|
+
async function record_yield(issue: number, reason: string, at: YieldAt): Promise<void> {
|
|
44
|
+
await run_event_stream_emit.emit(
|
|
45
|
+
run_event_stream.EVENT_KIND.NOTE,
|
|
46
|
+
`${issue_cite.plain(String(issue))}${YIELD_MARK}${reason}${YIELD_AT_MARK}${at}`,
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function is_yield_note(event: RunEvent): boolean {
|
|
51
|
+
return event.kind === run_event_stream.EVENT_KIND.NOTE && event.text.includes(YIELD_MARK)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Whether `event` is the withdrawal `run:merge` closed a yielded lane with, rather than one that takes
|
|
55
|
+
// the issue out of the run.
|
|
56
|
+
function is_yield(event: RunEvent): boolean {
|
|
57
|
+
return (
|
|
58
|
+
event.kind === run_event_stream.EVENT_KIND.WITHDRAW &&
|
|
59
|
+
event.text.includes(` withdrawn by ${YIELD_TEXT.name}:`)
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// An event that decides whether the issue's newest launch yielded: the launch, the yield, or a
|
|
64
|
+
// withdrawal that is not the yield's own close.
|
|
65
|
+
function is_deciding(event: RunEvent, issue: string): boolean {
|
|
66
|
+
if (run_event_scope.issue_named(event) !== issue) return false
|
|
67
|
+
if (event.kind === run_event_stream.EVENT_KIND.CHILD_LAUNCH) return true
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
is_yield_note(event) ||
|
|
71
|
+
(event.kind === run_event_stream.EVENT_KIND.WITHDRAW && !is_yield(event))
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Whether the issue's newest launch gave its lane back, and no one withdrew it since.
|
|
76
|
+
function is_yielded(events: ReadonlyArray<RunEvent>, issue: string): boolean {
|
|
77
|
+
const newest = events.findLast((event) => is_deciding(event, issue))
|
|
78
|
+
|
|
79
|
+
return newest !== undefined && is_yield_note(newest)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const lane_yield = { YIELD_REASON, YIELD_TEXT, is_yield, is_yielded, record_yield }
|
|
83
|
+
|
|
84
|
+
export { lane_yield }
|
|
85
|
+
export type { YieldAt }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { poll } from '#scripts/lib/poll'
|
|
2
2
|
import { run_ship_detach } from '#scripts/run/ship/run-ship-detach'
|
|
3
3
|
import { run_ship_probe } from '#scripts/run/ship/run-ship-probe'
|
|
4
4
|
|
|
@@ -12,19 +12,20 @@ async function current(issue: string): Promise<ShipReading | undefined> {
|
|
|
12
12
|
return repository === undefined ? undefined : run_ship_detach.read_result(repository, issue)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// The ship's ending once it leaves `running`, and `abnormal` once its result is gone.
|
|
16
|
+
async function settled(issue: string): Promise<Exclude<ShipEnding, 'none'> | undefined> {
|
|
17
|
+
const read = await current(issue)
|
|
18
|
+
if (read === undefined) return 'abnormal'
|
|
19
|
+
|
|
20
|
+
return read.result === 'running' ? undefined : read.result
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
async function wait_for_ship(issue: string, prior_launch: string | undefined): Promise<ShipEnding> {
|
|
16
|
-
|
|
24
|
+
const read = await current(issue)
|
|
17
25
|
if (read === undefined || read.launch_id === prior_launch) return 'none'
|
|
26
|
+
if (read.result !== 'running') return read.result
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
21
|
-
await sleep(POLL_MS)
|
|
22
|
-
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
23
|
-
read = await current(issue)
|
|
24
|
-
if (read === undefined) return 'abnormal'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return read.result
|
|
28
|
+
return await poll.wait_for(async () => await settled(issue), { interval_ms: POLL_MS })
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const openai_lane_ship = { current, wait_for_ship }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
2
2
|
import { json_value } from '#scripts/lib/json-value'
|
|
3
|
+
import { poll } from '#scripts/lib/poll'
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
|
|
5
6
|
const DECISION_PREFIX = 'josh-openai-lane-supervisor-decision-'
|
|
@@ -40,15 +41,12 @@ function cancel(lane_directory: string, nonce: string): boolean {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
async function is_approved(lane_directory: string, nonce: string): Promise<boolean> {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const decision = await poll.poll_for(() => read(lane_directory, nonce)?.decision, {
|
|
45
|
+
attempts: APPROVAL_POLL_LIMIT,
|
|
46
|
+
interval_ms: APPROVAL_POLL_MS,
|
|
47
|
+
})
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
await new Promise((resolve) => setTimeout(resolve, APPROVAL_POLL_MS))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return false
|
|
49
|
+
return decision === 'approved'
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
function remove(lane_directory: string, nonce: string): void {
|
|
@@ -4,6 +4,7 @@ import { process_identity } from '#scripts/josh/process-identity'
|
|
|
4
4
|
import { process_owner_schema } from '#scripts/josh/process-owner'
|
|
5
5
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
6
|
import { json_value } from '#scripts/lib/json-value'
|
|
7
|
+
import { poll } from '#scripts/lib/poll'
|
|
7
8
|
import { z } from 'zod'
|
|
8
9
|
import { openai_lane_supervisor_decision } from './openai-lane-supervisor-decision'
|
|
9
10
|
|
|
@@ -66,14 +67,10 @@ function active(lane_directory: string): SupervisorOwner | undefined {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
async function wait_for_active(lane_directory: string): Promise<SupervisorOwner | undefined> {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
await new Promise((resolve) => setTimeout(resolve, CLAIM_POLL_MS))
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return undefined
|
|
70
|
+
return await poll.poll_for(() => active(lane_directory), {
|
|
71
|
+
attempts: CLAIM_POLL_LIMIT,
|
|
72
|
+
interval_ms: CLAIM_POLL_MS,
|
|
73
|
+
})
|
|
77
74
|
}
|
|
78
75
|
|
|
79
76
|
// The hard link snapshots the exact owner inode. Several contenders may snapshot one stale owner,
|
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
3
3
|
import { agent_argv } from '#scripts/agent/agent-argv'
|
|
4
4
|
import type { AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
5
5
|
import { process_identity } from '#scripts/josh/process-identity'
|
|
6
|
+
import { poll } from '#scripts/lib/poll'
|
|
6
7
|
import { run_cut, type RunCut } from '#scripts/run/cut/run-cut'
|
|
7
8
|
import { detached_launch, type LaunchArgv } from '#scripts/run/detached-launch'
|
|
8
9
|
import { lane_child_invocation } from './lane-child-invocation'
|
|
@@ -142,10 +143,10 @@ async function wait_for_inherited_child(
|
|
|
142
143
|
child_pid: number,
|
|
143
144
|
child_process_start: string | undefined,
|
|
144
145
|
): Promise<void> {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
await poll.wait_for(
|
|
147
|
+
() => process_identity.is_same_process(child_pid, child_process_start) === false || undefined,
|
|
148
|
+
{ interval_ms: CHILD_POLL_MS },
|
|
149
|
+
)
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
async function recovered_resume(lane: LaneInfo, nonce: string): Promise<boolean | undefined> {
|
package/scripts/lib/poll.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// One
|
|
1
|
+
// One polling loop — bounded by attempts, or ended only by its read — and one sleep, in one place.
|
|
2
2
|
//
|
|
3
3
|
// **Three modules had already written the same three-line `sleep`** — `gh/git-pr-checks.ts`,
|
|
4
4
|
// `eval/eval-run.ts` and `propagate/propagate-publish.ts` — and the release command's tag watch
|
|
@@ -24,9 +24,6 @@ interface PollOptions {
|
|
|
24
24
|
sleeper?: (duration_ms: number) => Promise<void>
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// **True as soon as `is_done` answers true, false when the attempts run out.** The first attempt runs
|
|
28
|
-
// before any wait, so a condition already met costs nothing — which is the common case for a tag that
|
|
29
|
-
// was created while the command was doing something else.
|
|
30
27
|
// **The wait goes between attempts, never after the last one**, so the budget a caller computes from
|
|
31
28
|
// `attempts × interval_ms` is the budget it actually spends. The pre-existing merge-gate loop in
|
|
32
29
|
// `git-pr-checks.ts` guards its sleep the same way; extracted here so the loop keeps one branch.
|
|
@@ -39,21 +36,53 @@ async function wait_between(
|
|
|
39
36
|
if (attempt < attempts - 1) await wait(interval_ms)
|
|
40
37
|
}
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
type WaitOptions = Omit<PollOptions, 'attempts'>
|
|
40
|
+
|
|
41
|
+
// **The first value `read` answers, `undefined` when the attempts run out.** `undefined` is "not yet",
|
|
42
|
+
// so a read that reaches a verdict — success or failure — answers it and the loop ends there.
|
|
43
|
+
async function poll_for<T>(
|
|
44
|
+
read: () => Promise<T | undefined> | T | undefined,
|
|
45
|
+
options: PollOptions,
|
|
46
|
+
): Promise<T | undefined> {
|
|
43
47
|
const wait = options.sleeper ?? sleep
|
|
44
48
|
|
|
45
49
|
for (let attempt = 0; attempt < options.attempts; attempt += 1) {
|
|
46
50
|
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
47
|
-
|
|
51
|
+
const value = await read()
|
|
52
|
+
if (value !== undefined) return value
|
|
48
53
|
|
|
49
54
|
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
50
55
|
await wait_between(wait, options.interval_ms, attempt, options.attempts)
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
return
|
|
58
|
+
return undefined
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// **True as soon as `is_done` answers true, false when the attempts run out.** The first attempt runs
|
|
62
|
+
// before any wait, so a condition already met costs nothing — which is the common case for a tag that
|
|
63
|
+
// was created while the command was doing something else.
|
|
64
|
+
async function poll_until(
|
|
65
|
+
is_done: () => Promise<boolean> | boolean,
|
|
66
|
+
options: PollOptions,
|
|
67
|
+
): Promise<boolean> {
|
|
68
|
+
const is_reached = await poll_for(async () => ((await is_done()) ? true : undefined), options)
|
|
69
|
+
|
|
70
|
+
return is_reached === true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// **A wait that ends only when `read` answers** — a child process exiting, a ship leaving `running`.
|
|
74
|
+
// Its bound, if any, is the read's own: a wall-clock deadline the read answers with its failure.
|
|
75
|
+
async function wait_for<T>(
|
|
76
|
+
read: () => Promise<T | undefined> | T | undefined,
|
|
77
|
+
options: WaitOptions,
|
|
78
|
+
): Promise<T> {
|
|
79
|
+
const value = await poll_for(read, { ...options, attempts: Infinity })
|
|
80
|
+
if (value === undefined) throw new Error('an unbounded poll ran out of attempts')
|
|
81
|
+
|
|
82
|
+
return value
|
|
54
83
|
}
|
|
55
84
|
|
|
56
|
-
const poll = { poll_until, sleep }
|
|
85
|
+
const poll = { poll_for, poll_until, sleep, wait_for }
|
|
57
86
|
|
|
58
87
|
export { poll }
|
|
59
|
-
export type { PollOptions }
|
|
88
|
+
export type { PollOptions, WaitOptions }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Both steps run to their end before either's failure is thrown, so neither is left half-done behind
|
|
2
|
+
// the other's failure. The first step's failure is thrown when both failed.
|
|
3
|
+
async function settled_together(first: Promise<void>, second: Promise<void>): Promise<void> {
|
|
4
|
+
const [result, other] = await Promise.allSettled([first, second])
|
|
5
|
+
|
|
6
|
+
if (result.status === 'rejected') throw result.reason
|
|
7
|
+
if (other.status === 'rejected') throw other.reason
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { settled_together }
|