@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,274 @@
|
|
|
1
|
+
import { agent_argv } from '#scripts/agent/agent-argv'
|
|
2
|
+
import { agent_role_profile } from '#scripts/agent/agent-role-profile'
|
|
3
|
+
import { git_gh_issue_write } from '#scripts/gh/git-gh-issue-write'
|
|
4
|
+
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
5
|
+
import { issue_cite } from '#scripts/issue/issue-cite'
|
|
6
|
+
import { process_identity } from '#scripts/josh/process-identity'
|
|
7
|
+
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
8
|
+
import { detached_launch } from '#scripts/run/detached-launch'
|
|
9
|
+
import { run_event_overrun, type OverrunNote } from '#scripts/run/event/run-event-overrun'
|
|
10
|
+
import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
|
|
11
|
+
import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
|
|
12
|
+
import { lane_breakdown, type Breakdown } from './lane-breakdown'
|
|
13
|
+
import { lane_breakdown_implement } from './lane-breakdown-implement'
|
|
14
|
+
import { lane_breakdown_text, type Report } from './lane-breakdown-text'
|
|
15
|
+
import { lane_child_marker } from './lane-child-marker'
|
|
16
|
+
import { lane_investigation_switch } from './lane-investigation-switch'
|
|
17
|
+
import { lane_ledger, type LedgerEntry } from './lane-ledger'
|
|
18
|
+
import { lane_net_time } from './lane-net-time'
|
|
19
|
+
import { lane_overrun_prompt } from './lane-overrun-prompt'
|
|
20
|
+
import { lane_tail_stats } from './lane-tail-stats'
|
|
21
|
+
|
|
22
|
+
// A merged lane over the overrun threshold of net time is investigated for its causes
|
|
23
|
+
// (joshuafolkken/kit#4751). `run:merge` asks at each merge: a lane at or under the threshold — its long
|
|
24
|
+
// total spent waiting on a person or an outage included — stops here, before any record of it is read.
|
|
25
|
+
// An overrun launches a detached worker session that reads the lane's records and files each unfiled
|
|
26
|
+
// cause through `issue:file` — the causes one fix place shares as one issue, or folded into an open
|
|
27
|
+
// issue touching the same files (joshuafolkken/kit#4858) — so the next lane starts without waiting on
|
|
28
|
+
// it. The session carries the lane's child mark, which makes `issue:file` count its filings against the
|
|
29
|
+
// lane (`issue-file-cap.ts`), and `--found-during` names the same origin; its overrun mark records the
|
|
30
|
+
// filings as the investigation's, not the child's (joshuafolkken/kit#4880). The launch and the session's result both go on the run's
|
|
31
|
+
// event stream as notes. It runs only while `JOSH_LANE_INVESTIGATION` is on (`lane-investigation-switch.ts`);
|
|
32
|
+
// off by default, an overrun leaves a skipped note in place of the launch (joshuafolkken/kit#5044).
|
|
33
|
+
|
|
34
|
+
const { EVENT_KIND } = run_event_stream
|
|
35
|
+
const { DISABLED_REASON } = run_event_overrun
|
|
36
|
+
const LOG_PREFIX = 'josh-lane-overrun-'
|
|
37
|
+
const LOG_SUFFIX = '.log'
|
|
38
|
+
const NO_LEDGER = 'no ledger'
|
|
39
|
+
|
|
40
|
+
// A lane's net minutes and their breakdown by stage, or why there are none (joshuafolkken/kit#4818).
|
|
41
|
+
// `is_commented` says an earlier pass of the lane commented the breakdown on its Issue already;
|
|
42
|
+
// `entries` are the ledger rows the breakdown's details are read off (joshuafolkken/kit#5078).
|
|
43
|
+
type LaneNet =
|
|
44
|
+
| {
|
|
45
|
+
kind: 'minutes'
|
|
46
|
+
minutes: number
|
|
47
|
+
breakdown: Breakdown
|
|
48
|
+
entries: ReadonlyArray<LedgerEntry>
|
|
49
|
+
is_commented: boolean
|
|
50
|
+
}
|
|
51
|
+
| { kind: 'missing'; reason: string }
|
|
52
|
+
|
|
53
|
+
// A launch that failed is retried once, at the next `run:merge` (joshuafolkken/kit#4930).
|
|
54
|
+
const MAX_LAUNCH_FAILURES = 2
|
|
55
|
+
|
|
56
|
+
function overrun_notes(events: ReadonlyArray<RunEvent>): ReadonlyArray<OverrunNote> {
|
|
57
|
+
return events.flatMap((event) =>
|
|
58
|
+
event.kind === EVENT_KIND.NOTE ? (run_event_overrun.parse(event.text) ?? []) : [],
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// A pass that threw before it noted anything leaves a failure with no minutes.
|
|
63
|
+
function is_thrown(note: OverrunNote): boolean {
|
|
64
|
+
return note.kind === 'failed' && note.minutes === undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// A start note that launched no session.
|
|
68
|
+
function is_launch_failure(note: OverrunNote): boolean {
|
|
69
|
+
return note.kind === 'failed' && note.minutes !== undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function is_final(note: OverrunNote): boolean {
|
|
73
|
+
return !is_thrown(note) && !is_launch_failure(note)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// A merge `run:merge` collects again — a driver retry, a parent's hand run — finds its lane's note on
|
|
77
|
+
// the stream and launches no second session to file the same causes. A pass that threw before it
|
|
78
|
+
// noted anything leaves only its failure, which the retry answers; a launch that failed is answered
|
|
79
|
+
// once more.
|
|
80
|
+
function is_noted(events: ReadonlyArray<RunEvent>, issue: number): boolean {
|
|
81
|
+
const notes = overrun_notes(events).filter((note) => note.issue === issue)
|
|
82
|
+
const failures = notes.filter((note) => is_launch_failure(note)).length
|
|
83
|
+
|
|
84
|
+
return failures >= MAX_LAUNCH_FAILURES || notes.some((note) => is_final(note))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The earlier lanes whose latest note is a launch failure not yet retried; a retry that threw leaves
|
|
88
|
+
// its own note last, so it is not swept up again at every merge after.
|
|
89
|
+
function retried_lanes(events: ReadonlyArray<RunEvent>, issue: number): ReadonlyArray<number> {
|
|
90
|
+
const latest = new Map(overrun_notes(events).map((note) => [note.issue, note]))
|
|
91
|
+
|
|
92
|
+
latest.delete(issue)
|
|
93
|
+
|
|
94
|
+
return [...latest.values()]
|
|
95
|
+
.filter((note) => is_launch_failure(note) && !is_noted(events, note.issue))
|
|
96
|
+
.map((note) => note.issue)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// A pass that noted the lane before launched nothing it recorded, but it may have commented.
|
|
100
|
+
function is_commented(events: ReadonlyArray<RunEvent>, issue: number): boolean {
|
|
101
|
+
return overrun_notes(events).some((note) => note.issue === issue)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function net_in(path: string, events: ReadonlyArray<RunEvent>, issue: number): LaneNet {
|
|
105
|
+
const entries = lane_ledger.read_entries(path)
|
|
106
|
+
const minutes = lane_net_time.net_minutes(entries, events, issue)
|
|
107
|
+
const breakdown = lane_breakdown.of(entries, events, issue)
|
|
108
|
+
|
|
109
|
+
if (minutes === undefined || breakdown === undefined) {
|
|
110
|
+
return { kind: 'missing', reason: lane_net_time.missing_reason(entries, issue) }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { kind: 'minutes', minutes, breakdown, entries, is_commented: is_commented(events, issue) }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// The net time of a lane not yet noted, or `undefined` when its note is on the stream already.
|
|
117
|
+
async function net_of(issue: number): Promise<LaneNet | undefined> {
|
|
118
|
+
const events = await run_event_stream_emit.all_events()
|
|
119
|
+
|
|
120
|
+
if (is_noted(events, issue)) return undefined
|
|
121
|
+
|
|
122
|
+
const path = await lane_ledger.target()
|
|
123
|
+
|
|
124
|
+
return path === undefined ? { kind: 'missing', reason: NO_LEDGER } : net_in(path, events, issue)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// What the session reads, and the four facts it traces each cause to.
|
|
128
|
+
function reading(issue: number, cite: string, minutes: number): string {
|
|
129
|
+
const directories =
|
|
130
|
+
lane_breakdown_implement.transcript_directories(issue).join(', ') || 'none found'
|
|
131
|
+
|
|
132
|
+
return [
|
|
133
|
+
`Investigate why lane ${cite} took ${String(minutes)} minutes of net time (dispatch to merge, less`,
|
|
134
|
+
'its waits on a person and on outages). Leave this checkout as it is: write any file, an issue body',
|
|
135
|
+
'included, under the system temp directory, and never stage, commit or push.',
|
|
136
|
+
`Read the lane's transcripts (${directories}), its ship log (\`pnpm josh ship --log ${String(issue)}\`)`,
|
|
137
|
+
'and the run events (`pnpm josh run:event --from 0`), and find the causes that spent time other than',
|
|
138
|
+
'waiting. Trace each cause to what can fix it: the span it spent, the signal that led to the wrong call',
|
|
139
|
+
'or the wait (which command printed what), the `file:line` of the code or document that decided it, and',
|
|
140
|
+
'where to fix it.',
|
|
141
|
+
].join(' ')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// One line: `detached_launch` lets a prompt break lines, but a single line keeps the log header whole.
|
|
145
|
+
// The breakdown leads, so the search starts from the longest stage (joshuafolkken/kit#4995).
|
|
146
|
+
function prompt(issue: number, minutes: number, report: Report): string {
|
|
147
|
+
const reads = reading(issue, issue_cite.plain(issue), minutes)
|
|
148
|
+
const stages = lane_breakdown_text.line(report)
|
|
149
|
+
|
|
150
|
+
return `${stages} ${reads} ${lane_overrun_prompt.handling(issue, minutes)}`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function note_failure(issue: number): (note: string) => void {
|
|
154
|
+
return (note) => {
|
|
155
|
+
void run_event_stream_emit.emit(EVENT_KIND.NOTE, run_event_overrun.failed_text(issue, note))
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// A detached investigation session: its prompt, its `env` — the lane marks an overrun's filings count
|
|
160
|
+
// against that lane by, none for the baseline's — the prefix of its log's name, and the note a failed
|
|
161
|
+
// launch leaves.
|
|
162
|
+
interface Investigation {
|
|
163
|
+
env: Readonly<Record<string, string>>
|
|
164
|
+
prompt: string
|
|
165
|
+
log_prefix: string
|
|
166
|
+
on_failure: (note: string) => void
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// What became of the launch, in the words the launch note carries — the overrun's and the baseline
|
|
170
|
+
// investigation's (`lane-baseline.ts`) alike.
|
|
171
|
+
function launch(investigation: Investigation): string {
|
|
172
|
+
const cwd = process.cwd()
|
|
173
|
+
const built = agent_argv.resolve_in(investigation.prompt, agent_role_profile.WORKER, cwd)
|
|
174
|
+
|
|
175
|
+
if (built.kind === 'rejected') return `not investigated: ${built.note}`
|
|
176
|
+
|
|
177
|
+
const request = {
|
|
178
|
+
argv: built.argv,
|
|
179
|
+
cwd,
|
|
180
|
+
env: investigation.env,
|
|
181
|
+
log_path: stamp_file.stamp_path(investigation.log_prefix, PROJECT_ROOT, LOG_SUFFIX),
|
|
182
|
+
profile: built.profile,
|
|
183
|
+
}
|
|
184
|
+
const result = detached_launch.launch(request, investigation.on_failure)
|
|
185
|
+
|
|
186
|
+
return result.kind === 'launched'
|
|
187
|
+
? run_event_overrun.investigating(result.pid, process_identity.read_start(result.pid))
|
|
188
|
+
: `not investigated: ${result.note}`
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function launched(issue: number, minutes: number, report: Report): string {
|
|
192
|
+
return launch({
|
|
193
|
+
env: lane_child_marker.overrun_env_for(String(issue)),
|
|
194
|
+
prompt: prompt(issue, minutes, report),
|
|
195
|
+
log_prefix: `${LOG_PREFIX}${String(issue)}-`,
|
|
196
|
+
on_failure: note_failure(issue),
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
type OverranNet = Extract<LaneNet, { kind: 'minutes' }>
|
|
201
|
+
|
|
202
|
+
// The breakdown goes on the lane's Issue before the session starts, once across the lane's passes; a
|
|
203
|
+
// refused comment costs the comment, not the investigation.
|
|
204
|
+
async function overran_text(issue: number, net: OverranNet): Promise<string> {
|
|
205
|
+
const report = lane_breakdown_text.report_of(issue, net.breakdown, net.entries)
|
|
206
|
+
const comment = lane_breakdown_text.comment(issue, net.minutes, report)
|
|
207
|
+
|
|
208
|
+
if (!net.is_commented) await git_gh_issue_write.issue_try_comment(String(issue), comment)
|
|
209
|
+
|
|
210
|
+
const outcome = launched(issue, net.minutes, report)
|
|
211
|
+
const longest = lane_breakdown.longest_of(net.breakdown)
|
|
212
|
+
|
|
213
|
+
return run_event_overrun.started_text(issue, net.minutes, outcome, longest)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// The note every merged lane leaves: its net minutes at or under the threshold, why it has none, or the
|
|
217
|
+
// launch of its overrun's investigation.
|
|
218
|
+
async function note_text(issue: number, net: LaneNet): Promise<string> {
|
|
219
|
+
if (net.kind === 'missing') return run_event_overrun.skipped_text(issue, net.reason)
|
|
220
|
+
// The threshold sets no priority: each filing is ranked by its causes' reducible minutes
|
|
221
|
+
// (joshuafolkken/kit#4928). `lane:stats` counts the lanes over it on dispatch-to-merge time, not
|
|
222
|
+
// on this net time.
|
|
223
|
+
const is_over = lane_tail_stats.is_overrun(net.minutes)
|
|
224
|
+
|
|
225
|
+
if (!is_over) return run_event_overrun.within_text(issue, net.minutes)
|
|
226
|
+
|
|
227
|
+
// The switch off, an overrun leaves its skip: no breakdown comment and no launch.
|
|
228
|
+
if (!lane_investigation_switch.is_on()) {
|
|
229
|
+
return run_event_overrun.skipped_text(issue, DISABLED_REASON)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return await overran_text(issue, net)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function investigate_lane(issue: number): Promise<void> {
|
|
236
|
+
const net = await net_of(issue)
|
|
237
|
+
|
|
238
|
+
if (net === undefined) return
|
|
239
|
+
|
|
240
|
+
await run_event_stream_emit.emit(EVENT_KIND.NOTE, await note_text(issue, net))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Never fails the merge it follows: an error is a note on the stream, not a refused merge.
|
|
244
|
+
async function guarded(issue: number, pass: (issue: number) => Promise<void>): Promise<void> {
|
|
245
|
+
try {
|
|
246
|
+
await pass(issue)
|
|
247
|
+
} catch (error) {
|
|
248
|
+
note_failure(issue)(detached_launch.note_of(error))
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// The merged lane, then each earlier lane whose launch failed once.
|
|
253
|
+
async function investigate_with_retries(issue: number): Promise<void> {
|
|
254
|
+
await investigate_lane(issue)
|
|
255
|
+
const retried = retried_lanes(await run_event_stream_emit.all_events(), issue)
|
|
256
|
+
|
|
257
|
+
for (const lane of retried) {
|
|
258
|
+
// eslint-disable-next-line no-await-in-loop -- one pass at a time appends to the stream, as the merged lane's did
|
|
259
|
+
await guarded(lane, investigate_lane)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// With the switch off a lane still notes its net minutes, which `run:board` draws; no earlier lane is
|
|
264
|
+
// retried.
|
|
265
|
+
async function investigate(issue: number): Promise<void> {
|
|
266
|
+
const pass = lane_investigation_switch.is_on() ? investigate_with_retries : investigate_lane
|
|
267
|
+
|
|
268
|
+
await guarded(issue, pass)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const lane_overrun = { investigate, launch }
|
|
272
|
+
|
|
273
|
+
export type { Investigation }
|
|
274
|
+
export { lane_overrun }
|
|
@@ -57,6 +57,16 @@ function is_lane_root(directory: string, environment: LaneEnvironment): boolean
|
|
|
57
57
|
return DEFAULT_LANE_ROOT_PATTERN.test(path.basename(directory))
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Claude Code's own worktrees (`EnterWorktree`, an `isolation: "worktree"` subagent) sit at
|
|
61
|
+
// `<checkout>/.claude/worktrees/<name>`, and a hook launched in one runs there
|
|
62
|
+
// (joshuafolkken/kit#4617). The session is still the one its checkout started, so a directory under
|
|
63
|
+
// one reads as the checkout that holds it.
|
|
64
|
+
const CLAUDE_WORKTREES_PATTERN = /[/\\]\.claude[/\\]worktrees[/\\][^/\\]+(?:[/\\].*)?$/u
|
|
65
|
+
|
|
66
|
+
function checkout_of(directory: string): string {
|
|
67
|
+
return directory.replace(CLAUDE_WORKTREES_PATTERN, '')
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
// **The inverse of `lane_directory`, and the one lane test that needs no git.** `lane_registry`
|
|
61
71
|
// answers the same question authoritatively by parsing `git worktree list`, but that read is
|
|
62
72
|
// asynchronous and a `PreToolUse` guard is synchronous by contract — so a guard that has to know
|
|
@@ -71,7 +81,7 @@ function lane_issue_of(
|
|
|
71
81
|
directory: string,
|
|
72
82
|
environment: LaneEnvironment = process.env,
|
|
73
83
|
): string | undefined {
|
|
74
|
-
const resolved = path.resolve(directory)
|
|
84
|
+
const resolved = checkout_of(path.resolve(directory))
|
|
75
85
|
const issue = path.basename(resolved)
|
|
76
86
|
|
|
77
87
|
if (!issue_number_shape.ISSUE_NUMBER_PATTERN.test(issue)) return undefined
|
|
@@ -79,6 +89,17 @@ function lane_issue_of(
|
|
|
79
89
|
return is_lane_root(path.dirname(resolved), environment) ? issue : undefined
|
|
80
90
|
}
|
|
81
91
|
|
|
92
|
+
// The lane `directory` (this process's own by default) sits in, as a number; `undefined` outside a
|
|
93
|
+
// lane — the `issue` a ledger row written from inside a lane carries.
|
|
94
|
+
function issue_here(
|
|
95
|
+
directory: string = process.cwd(),
|
|
96
|
+
environment: LaneEnvironment = process.env,
|
|
97
|
+
): number | undefined {
|
|
98
|
+
const lane = lane_issue_of(directory, environment)
|
|
99
|
+
|
|
100
|
+
return lane === undefined ? undefined : Number(lane)
|
|
101
|
+
}
|
|
102
|
+
|
|
82
103
|
function lane_branch(issue: string): string {
|
|
83
104
|
return `${issue}${LANE_BRANCH_SUFFIX}`
|
|
84
105
|
}
|
|
@@ -98,8 +119,10 @@ const lane_paths = {
|
|
|
98
119
|
LANE_BRANCH_SUFFIX,
|
|
99
120
|
LANE_ROOT_KEY,
|
|
100
121
|
SEAT_LOCK_DIR,
|
|
122
|
+
checkout_of,
|
|
101
123
|
default_lane_root,
|
|
102
124
|
is_lane_entry,
|
|
125
|
+
issue_here,
|
|
103
126
|
lane_branch,
|
|
104
127
|
lane_directory,
|
|
105
128
|
lane_issue_of,
|
|
@@ -4,6 +4,7 @@ import { issue_cite } from '#scripts/issue/issue-cite'
|
|
|
4
4
|
import { issue_number_shape } from '#scripts/issue/issue-number-shape'
|
|
5
5
|
import { run_event_stream } from '#scripts/run/event/run-event-stream'
|
|
6
6
|
import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
|
|
7
|
+
import { lane_ledger_write } from './lane-ledger-write'
|
|
7
8
|
import { lane_pause, type LaneSignalled } from './lane-pause'
|
|
8
9
|
import type { LaneProcesses } from './lane-reap'
|
|
9
10
|
import { lane_registry } from './lane-registry'
|
|
@@ -14,7 +15,8 @@ import { lane_state } from './lane-state'
|
|
|
14
15
|
// caller never takes a pause that stopped nothing as done — a detached resident signalled alone counts
|
|
15
16
|
// as nothing. A lane whose detached processes could not be read exits non-zero too, since a resume
|
|
16
17
|
// may have left one of them stopped. A lane whose child was signalled has the change written to its state record and to the
|
|
17
|
-
// run's event stream, so who halted it is on record
|
|
18
|
+
// run's event stream, so who halted it is on record, and to the lane ledger, so `lane:stats` leaves the
|
|
19
|
+
// stopped time out of the lane's implement sub-stages (joshuafolkken/kit#5168).
|
|
18
20
|
|
|
19
21
|
const ARGV_OFFSET = 2
|
|
20
22
|
const SUCCESS_EXIT_CODE = 0
|
|
@@ -73,6 +75,7 @@ async function record(verb: Verb, issue: string): Promise<void> {
|
|
|
73
75
|
const command = `lane:${verb}`
|
|
74
76
|
|
|
75
77
|
await lane_state.record_change({ issue, state: VERBS[verb].state, command })
|
|
78
|
+
await lane_ledger_write.record_lane_state({ issue: Number(issue), state: VERBS[verb].state })
|
|
76
79
|
await run_event_stream_emit.emit(
|
|
77
80
|
run_event_stream.EVENT_KIND.LANE_STATE,
|
|
78
81
|
`${issue_cite.plain(issue)} ${VERBS[verb].done} by ${command}`,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lane_ledger,
|
|
3
|
+
type DispatchEntry,
|
|
4
|
+
type LaneStateChangeEntry,
|
|
5
|
+
type LedgerEntry,
|
|
6
|
+
} from './lane-ledger'
|
|
7
|
+
|
|
8
|
+
// The spans each lane stood paused, read off the `lane-state` lines `lane:pause` and `lane:resume`
|
|
9
|
+
// append to the ledger (joshuafolkken/kit#5168). A SIGSTOP leaves the call in flight on the clock, so a
|
|
10
|
+
// measurement of a lane's time takes its overlap with these out: `lane:pause --all` once put seven
|
|
11
|
+
// hours into one plan call and one check call.
|
|
12
|
+
//
|
|
13
|
+
// **A pause opens a span, the lane's next resume or dispatch closes it** — `lane:restart` and a
|
|
14
|
+
// re-dispatch start a fresh child without a resume; a pause with neither yet stays open, and a
|
|
15
|
+
// resume with no pause before it closes nothing.
|
|
16
|
+
|
|
17
|
+
const PAUSED = 'paused'
|
|
18
|
+
const LANE_ISSUE_PATTERN = /-(?<issue>\d+)$/u
|
|
19
|
+
|
|
20
|
+
interface PausedSpan {
|
|
21
|
+
from_ms: number
|
|
22
|
+
to_ms: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Each lane's paused spans, by issue number.
|
|
26
|
+
type PausedSpans = ReadonlyMap<number, ReadonlyArray<PausedSpan>>
|
|
27
|
+
|
|
28
|
+
interface Walk {
|
|
29
|
+
spans: Array<PausedSpan>
|
|
30
|
+
open_ms: number | undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// A ledger line that opens or closes a lane's paused span.
|
|
34
|
+
type Change = LaneStateChangeEntry | DispatchEntry
|
|
35
|
+
|
|
36
|
+
const NONE: ReadonlyArray<PausedSpan> = []
|
|
37
|
+
|
|
38
|
+
function is_change(entry: LedgerEntry): entry is Change {
|
|
39
|
+
return entry.kind === lane_ledger.KIND.LANE_STATE || entry.kind === lane_ledger.KIND.DISPATCH
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function is_pause(change: Change): boolean {
|
|
43
|
+
return change.kind === lane_ledger.KIND.LANE_STATE && change.state === PAUSED
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function step(walk: Walk, change: Change): Walk {
|
|
47
|
+
const at_ms = Date.parse(change.at)
|
|
48
|
+
|
|
49
|
+
if (is_pause(change)) return { spans: walk.spans, open_ms: walk.open_ms ?? at_ms }
|
|
50
|
+
if (walk.open_ms === undefined) return walk
|
|
51
|
+
|
|
52
|
+
return { spans: [...walk.spans, { from_ms: walk.open_ms, to_ms: at_ms }], open_ms: undefined }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// One lane's changes, oldest first, folded into its spans.
|
|
56
|
+
function spans_of(changes: ReadonlyArray<Change>): Array<PausedSpan> {
|
|
57
|
+
let walk: Walk = { spans: [], open_ms: undefined }
|
|
58
|
+
|
|
59
|
+
for (const change of changes) walk = step(walk, change)
|
|
60
|
+
if (walk.open_ms === undefined) return walk.spans
|
|
61
|
+
|
|
62
|
+
return [...walk.spans, { from_ms: walk.open_ms, to_ms: Infinity }]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function by_lane(entries: ReadonlyArray<LedgerEntry>): PausedSpans {
|
|
66
|
+
const changes = entries
|
|
67
|
+
.filter((entry) => is_change(entry))
|
|
68
|
+
.toSorted((left, right) => Date.parse(left.at) - Date.parse(right.at))
|
|
69
|
+
const issues = [...new Set(changes.map((change) => change.issue))]
|
|
70
|
+
|
|
71
|
+
return new Map(
|
|
72
|
+
issues.map((issue) => [issue, spans_of(changes.filter((change) => change.issue === issue))]),
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The spans of the lane a transcript directory (`<lane-root-slug>-<issue>`) belongs to.
|
|
77
|
+
function of_lane(spans: PausedSpans, lane: string): ReadonlyArray<PausedSpan> {
|
|
78
|
+
const issue = LANE_ISSUE_PATTERN.exec(lane)?.groups?.['issue']
|
|
79
|
+
|
|
80
|
+
return issue === undefined ? NONE : (spans.get(Number(issue)) ?? NONE)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// How much of `from_ms`…`to_ms` the spans cover.
|
|
84
|
+
function overlap_ms(spans: ReadonlyArray<PausedSpan>, from_ms: number, to_ms: number): number {
|
|
85
|
+
let sum = 0
|
|
86
|
+
|
|
87
|
+
for (const span of spans) {
|
|
88
|
+
sum += Math.max(0, Math.min(to_ms, span.to_ms) - Math.max(from_ms, span.from_ms))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return sum
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const lane_paused_spans = { by_lane, of_lane, overlap_ms }
|
|
95
|
+
|
|
96
|
+
export type { PausedSpan, PausedSpans }
|
|
97
|
+
export { lane_paused_spans }
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { auto_ok_cli } from '#scripts/auto-ok/auto-ok-cli'
|
|
2
|
+
import { backlog_overlap, type Clash } from '#scripts/backlog/backlog-overlap'
|
|
3
|
+
import { backlog_plan_comment } from '#scripts/backlog/backlog-plan-comment'
|
|
4
|
+
import type { OpenIssueData } from '#scripts/git/git-schemas'
|
|
5
|
+
import { has_any_label, IN_PROGRESS_LABEL } from '#scripts/issue/issue-labels'
|
|
6
|
+
import { issue_number_shape } from '#scripts/issue/issue-number-shape'
|
|
7
|
+
import { session_cite } from '#scripts/issue/session-cite'
|
|
8
|
+
import { run_hold_cli } from '#scripts/run/hold/run-hold-cli'
|
|
9
|
+
import { lane_plan, type LanePlan } from './lane-plan'
|
|
10
|
+
import { lane_yield, type YieldAt } from './lane-yield'
|
|
11
|
+
|
|
12
|
+
// The check `josh lane:plan <issue> <path>...` runs (`lane-plan-cli.ts`) — record the files a lane child
|
|
13
|
+
// plans to edit, and answer once whether a running lane that planned earlier declares or plans one of
|
|
14
|
+
// them (joshuafolkken/kit#4864).
|
|
15
|
+
//
|
|
16
|
+
// **The check happens before the first edit**, so a conflict the bodies did not show is met while the
|
|
17
|
+
// later lane has nothing to throw away, not at its ship. **A lane that finds a holder gives its lane
|
|
18
|
+
// back** rather than waiting for the holder to end (joshuafolkken/kit#4926): polling kept the seat taken
|
|
19
|
+
// while nothing moved, which made two lanes one. It notes its yield (`lane-yield.ts`), keeps its plan as
|
|
20
|
+
// yielded, releases its hold, prints `yield` and the child ends; the issue is offered again once the
|
|
21
|
+
// holder's lane ends. A document under `docs/` is never a clash (`backlog-overlap.ts` →
|
|
22
|
+
// `is_shared_document`).
|
|
23
|
+
//
|
|
24
|
+
// **`--probe` answers the same question and records nothing** (joshuafolkken/kit#5211) — no plan, no
|
|
25
|
+
// yield, no release. **`run:entry` gives the lane back at the entry** in a lane child, before any
|
|
26
|
+
// reading, on the paths dispatch gated on (`yield_at_entry`), so a holder that planned them since
|
|
27
|
+
// dispatch takes the lane back before the investigation the later `lane:plan` would have thrown away.
|
|
28
|
+
|
|
29
|
+
const SUCCESS_EXIT_CODE = 0
|
|
30
|
+
const FAILURE_EXIT_CODE = 1
|
|
31
|
+
const YIELD_EXIT_CODE = 2
|
|
32
|
+
const RELEASE_FLAG = '--release'
|
|
33
|
+
const RUNNING = new Set([IN_PROGRESS_LABEL])
|
|
34
|
+
const UNREAD = 'unread'
|
|
35
|
+
|
|
36
|
+
// What the overlap check found: the clash, none, or a listing it could not read.
|
|
37
|
+
type Probe = Clash | typeof UNREAD | undefined
|
|
38
|
+
|
|
39
|
+
function plan_of(argv: ReadonlyArray<string>, now: Date): LanePlan | undefined {
|
|
40
|
+
const [issue = '', ...paths] = argv
|
|
41
|
+
|
|
42
|
+
const claims = [...backlog_overlap.path_claims(paths)]
|
|
43
|
+
|
|
44
|
+
if (!issue_number_shape.is_issue_number(issue) || claims.length === 0) return undefined
|
|
45
|
+
|
|
46
|
+
return { issue: Number(issue), paths: claims, planned_at: now.toISOString() }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The opted-in listing — the running lanes carry the pickup label, so it holds their bodies.
|
|
50
|
+
async function opted_in(): Promise<ReadonlyArray<OpenIssueData> | undefined> {
|
|
51
|
+
const read = await auto_ok_cli.fetch_opted_in()
|
|
52
|
+
|
|
53
|
+
return read.kind === 'read' ? read.issues : undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function running_of(issues: ReadonlyArray<OpenIssueData>): ReadonlyArray<OpenIssueData> {
|
|
57
|
+
return issues.filter((issue) => has_any_label(issue.labels, RUNNING))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function clash_among(
|
|
61
|
+
plan: LanePlan,
|
|
62
|
+
issues: ReadonlyArray<OpenIssueData>,
|
|
63
|
+
directory: string,
|
|
64
|
+
): Clash | undefined {
|
|
65
|
+
const declared = lane_plan.with_plans(backlog_overlap.declared_of(issues), directory)
|
|
66
|
+
|
|
67
|
+
return lane_plan.clash_of(plan, declared, directory)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function overlap_text(issue: number, clash: Clash): string {
|
|
71
|
+
const holder = session_cite.issue(clash.holder)
|
|
72
|
+
|
|
73
|
+
return `${session_cite.issue(issue)}'s planned \`${clash.path}\` overlaps \`${clash.claimed}\`, which ${holder} declares or plans`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The yield goes on the stream before the child is told to end, so `run:merge` books it `yielded`. The
|
|
77
|
+
// plan is kept as yielded (`lane_plan.release`) and the child's hold released, so it can end with no
|
|
78
|
+
// edit; `yield` is printed last, after the release's own verdict.
|
|
79
|
+
async function give_back(
|
|
80
|
+
plan: LanePlan,
|
|
81
|
+
clash: Clash,
|
|
82
|
+
directory: string,
|
|
83
|
+
at: YieldAt,
|
|
84
|
+
): Promise<number> {
|
|
85
|
+
const overlap = overlap_text(plan.issue, clash)
|
|
86
|
+
|
|
87
|
+
lane_plan.record({ ...plan, yielded: true }, directory)
|
|
88
|
+
await lane_yield.record_yield(plan.issue, overlap, at)
|
|
89
|
+
await run_hold_cli.run([RELEASE_FLAG, String(plan.issue)])
|
|
90
|
+
console.error(
|
|
91
|
+
`${overlap}, so it gives its lane back: end the session now, with no edit — \`run:merge\` closes the lane, and the issue is offered again once that lane ends.`,
|
|
92
|
+
)
|
|
93
|
+
console.info('yield')
|
|
94
|
+
|
|
95
|
+
return YIELD_EXIT_CODE
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function clear_answer(): number {
|
|
99
|
+
console.info('clear')
|
|
100
|
+
|
|
101
|
+
return SUCCESS_EXIT_CODE
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function clear(plan: LanePlan, directory: string): number {
|
|
105
|
+
lane_plan.record({ ...plan, cleared: true, yielded: undefined }, directory)
|
|
106
|
+
|
|
107
|
+
return clear_answer()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// An unread listing proves no holder absent, so it holds the edit back for one more ask.
|
|
111
|
+
function unread(): number {
|
|
112
|
+
console.error(auto_ok_cli.UNREADABLE_MESSAGE)
|
|
113
|
+
console.info('wait')
|
|
114
|
+
|
|
115
|
+
return FAILURE_EXIT_CODE
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The overlap check alone, writing nothing — what `--probe` answers and `check_plan` acts on.
|
|
119
|
+
async function probe(plan: LanePlan, directory: string): Promise<Probe> {
|
|
120
|
+
const issues = await opted_in()
|
|
121
|
+
|
|
122
|
+
return issues === undefined ? UNREAD : clash_among(plan, running_of(issues), directory)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function check_plan(plan: LanePlan, directory: string): Promise<number> {
|
|
126
|
+
lane_plan.record(plan, directory)
|
|
127
|
+
const found = await probe(plan, directory)
|
|
128
|
+
|
|
129
|
+
if (found === UNREAD) return unread()
|
|
130
|
+
|
|
131
|
+
return found === undefined
|
|
132
|
+
? clear(plan, directory)
|
|
133
|
+
: await give_back(plan, found, directory, 'summary')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// `--probe`'s answer: the same tokens and exit codes as the plan's, with nothing written.
|
|
137
|
+
async function probe_answer(plan: LanePlan, directory: string): Promise<number> {
|
|
138
|
+
const found = await probe(plan, directory)
|
|
139
|
+
|
|
140
|
+
if (found === UNREAD) return unread()
|
|
141
|
+
if (found === undefined) return clear_answer()
|
|
142
|
+
|
|
143
|
+
console.error(`${overlap_text(plan.issue, found)}.`)
|
|
144
|
+
console.info('yield')
|
|
145
|
+
|
|
146
|
+
return YIELD_EXIT_CODE
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// A lane that plans again — after a yield, or to add a file — keeps the place in line its first plan
|
|
150
|
+
// took: a fresh time would hand the files it already holds to a lane that planned after it. A yield
|
|
151
|
+
// stays marked until the plan clears, so a check the listing could not answer keeps the plan its lane
|
|
152
|
+
// yielded on.
|
|
153
|
+
function placed(plan: LanePlan, directory: string): LanePlan {
|
|
154
|
+
const recorded = lane_plan.read(plan.issue, directory)
|
|
155
|
+
if (recorded === undefined) return plan
|
|
156
|
+
|
|
157
|
+
return { ...plan, planned_at: recorded.planned_at, yielded: recorded.yielded }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The paths `issue` declares in the scope dispatch gated on — its recorded plan, else its plan comment,
|
|
161
|
+
// else its body (`backlog-plan-comment.ts`) — read off the same listing the probe reads the holders from.
|
|
162
|
+
async function entry_plan(
|
|
163
|
+
issue: number,
|
|
164
|
+
issues: ReadonlyArray<OpenIssueData>,
|
|
165
|
+
directory: string,
|
|
166
|
+
): Promise<LanePlan | undefined> {
|
|
167
|
+
const own = issues.filter((candidate) => candidate.number === issue)
|
|
168
|
+
const declared = await backlog_plan_comment.declared_of(own, directory)
|
|
169
|
+
const plan = plan_of([String(issue), ...(declared.get(issue) ?? [])], new Date())
|
|
170
|
+
|
|
171
|
+
return plan === undefined ? undefined : placed(plan, directory)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function entry_yield(
|
|
175
|
+
issue: number,
|
|
176
|
+
issues: ReadonlyArray<OpenIssueData>,
|
|
177
|
+
directory: string,
|
|
178
|
+
): Promise<boolean> {
|
|
179
|
+
const plan = await entry_plan(issue, issues, directory)
|
|
180
|
+
const found = plan === undefined ? undefined : clash_among(plan, running_of(issues), directory)
|
|
181
|
+
|
|
182
|
+
if (plan === undefined || found === undefined) return false
|
|
183
|
+
|
|
184
|
+
await give_back(plan, found, directory, 'entry')
|
|
185
|
+
|
|
186
|
+
return true
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// **The entry probe** `run:entry` runs in a lane child before the investigation: a clash gives the lane back
|
|
190
|
+
// there, noted `at: entry`, the way `lane:plan` would after the summary. A clear answer, an unread
|
|
191
|
+
// listing or no declared path writes nothing — the later `lane:plan` still checks at the first edit.
|
|
192
|
+
async function yield_at_entry(issue: number, directory: string = process.cwd()): Promise<boolean> {
|
|
193
|
+
const issues = await opted_in()
|
|
194
|
+
|
|
195
|
+
return issues === undefined ? false : await entry_yield(issue, issues, directory)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const lane_plan_check = {
|
|
199
|
+
YIELD_EXIT_CODE,
|
|
200
|
+
check_plan,
|
|
201
|
+
placed,
|
|
202
|
+
plan_of,
|
|
203
|
+
probe_answer,
|
|
204
|
+
yield_at_entry,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export { lane_plan_check }
|