@joshuafolkken/kit 1.1178.0 → 1.1240.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +7 -3
- package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
- package/.claude/skills/workflow-commands/chain-rule.md +1 -1
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +10 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -1
- package/.claude/skills/workflow-commands/latest-gate.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
- package/.claude/skills/workflow-commands/prerequisite.md +2 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
- package/.codex/hooks.json +2 -2
- package/.cursorrules +6 -41
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/auto-tag.yml +3 -4
- package/.github/workflows/ci.yml +32 -31
- package/.github/workflows/dependabot-auto-merge.yml +13 -6
- package/.github/workflows/publish.yml +50 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +44 -38
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
- package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
- package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
- package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
- package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
- package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
- package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
- package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +203 -179
- package/dist/hooks/investigation-guard.js +6 -5
- package/dist/hooks/pretool-guard.js +12 -10
- package/dist/hooks/session-lang.js +2 -4
- package/dist/hooks/stop-guard.js +66 -72
- package/dist/josh.js +1088 -78
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-template.md +1 -0
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +16 -5
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-scope.ts +1 -1
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +3 -1
- package/scripts/build/build-commands.ts +66 -0
- package/scripts/build/build-hooks.ts +2 -1
- package/scripts/build/build-library.ts +12 -3
- package/scripts/build/build.ts +70 -0
- package/scripts/build/local-bin.ts +3 -13
- package/scripts/delegation/investigation-reads.ts +3 -1
- package/scripts/discovery/repo-origin.ts +5 -5
- package/scripts/doctor/doctor-consumer.ts +2 -2
- package/scripts/doctor/doctor-io.ts +2 -3
- package/scripts/document/capped-print-part.ts +30 -0
- package/scripts/document/capped-print.ts +3 -4
- package/scripts/document/document-byte-budget.ts +11 -2
- package/scripts/document/document-scan.ts +117 -0
- package/scripts/document/document-section.ts +143 -16
- package/scripts/document/entry-read-budget.ts +5 -3
- package/scripts/document/entry-read-set.ts +10 -89
- package/scripts/document/read-set-cost.ts +98 -0
- package/scripts/epic/epic-add-body.ts +1 -0
- package/scripts/epic/epic-audit-checks.ts +1 -1
- package/scripts/epic/epic-audit-rationale.ts +4 -3
- package/scripts/epic/epic-bundle-cli.ts +14 -3
- package/scripts/epic/epic-bundle-poll.ts +41 -0
- package/scripts/epic/epic-busy.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +3 -2
- package/scripts/epic/epic-classify.ts +1 -1
- package/scripts/epic/epic-cli-argv.ts +78 -46
- package/scripts/epic/epic-cli-remove.ts +11 -19
- package/scripts/epic/epic-cli.ts +74 -73
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +5 -5
- package/scripts/epic/epic-graph.ts +1 -1
- package/scripts/epic/epic-index.ts +1 -1
- package/scripts/epic/epic-issue.ts +2 -6
- package/scripts/epic/epic-lane-offer.ts +2 -2
- package/scripts/epic/epic-next.ts +29 -42
- package/scripts/epic/epic-solo-stale.ts +1 -1
- package/scripts/epic/epic-triage.ts +8 -4
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/followup/git-followup-finish.ts +2 -2
- package/scripts/followup/git-followup-label.ts +2 -2
- package/scripts/followup/git-followup-merged.ts +2 -2
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +4 -129
- package/scripts/gate/core-admission.ts +162 -0
- package/scripts/gate/core-budget.ts +190 -68
- package/scripts/gate/gate-plan.ts +80 -7
- package/scripts/gate/gate-report.ts +28 -9
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/machine-capacity.ts +175 -0
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +34 -10
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-pr-read.ts +64 -19
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr.ts +5 -1
- package/scripts/gh/git-pr-ai-review.ts +1 -1
- package/scripts/gh/git-pr-checks-eval.ts +1 -3
- package/scripts/gh/git-pr-checks-parse.ts +1 -1
- package/scripts/gh/git-pr-checks-watch.ts +6 -5
- package/scripts/gh/git-pr-checks.ts +7 -5
- package/scripts/gh/git-pr-coderabbit.ts +1 -1
- package/scripts/gh/git-pr-error.ts +1 -3
- package/scripts/gh/git-pr.ts +9 -13
- package/scripts/gh/github-issue-url.ts +2 -2
- package/scripts/git/constants.ts +2 -6
- package/scripts/git/git-command.ts +20 -218
- package/scripts/git/git-diff-reads.ts +225 -0
- package/scripts/git/git-error.ts +4 -12
- package/scripts/git/git-pre-push-hook.ts +139 -0
- package/scripts/git/git-prompt.ts +2 -6
- package/scripts/git/git-spawn-sync.ts +3 -1
- package/scripts/git/git-spawn.ts +1 -1
- package/scripts/git/git-staging.ts +4 -6
- package/scripts/git/git-status.ts +3 -9
- package/scripts/hooks/codex-hook-adapter.ts +1 -1
- package/scripts/hooks/edited-cspell.ts +2 -4
- package/scripts/hooks/format-edited-file.ts +3 -12
- package/scripts/hooks/pre-push-unit.ts +31 -9
- package/scripts/hooks/reserved-run.ts +2 -2
- package/scripts/hooks/run-hook.sh +5 -4
- package/scripts/hooks/stop-guard.ts +5 -3
- package/scripts/init/init-actions.ts +0 -5
- package/scripts/init/init-ai-copy.ts +13 -2
- package/scripts/init/init-logic-json-merge.ts +19 -0
- package/scripts/init/init-logic-secretlint.ts +7 -7
- package/scripts/init/init-logic-workspace.ts +41 -2
- package/scripts/init/init-logic.ts +16 -26
- package/scripts/init/init.ts +10 -16
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/package-manager-pin.ts +35 -0
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +32 -7
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +72 -22
- package/scripts/issue/issue-file.ts +29 -1
- package/scripts/issue/issue-labels.ts +13 -0
- package/scripts/issue/issue-release-cli.ts +155 -0
- package/scripts/issue/issue-release.ts +58 -0
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +14 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +18 -0
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-issue.ts +8 -1
- package/scripts/josh/josh-commands-lane.ts +24 -1
- package/scripts/josh/josh-commands-maintenance.ts +6 -6
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +34 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +12 -4
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +15 -1
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +44 -30
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-open.ts +5 -5
- package/scripts/lane/lane-paths.ts +13 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-registry.ts +11 -0
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-stray.ts +140 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/release/github-release-environment.ts +1 -0
- package/scripts/release/github-release-runs.ts +115 -0
- package/scripts/release/github-release.ts +13 -67
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +1 -1
- package/scripts/rules/protected-files.ts +59 -4
- package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
- package/scripts/rules/shell-body-trigger.ts +1 -1
- package/scripts/rules/stop-rules.ts +18 -7
- package/scripts/rules/test-declared-commit.ts +4 -4
- package/scripts/rules/wip-cap.ts +63 -0
- package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
- package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
- package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
- package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
- package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
- package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +7 -4
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
- package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
- package/scripts/run/ship/run-ship-lock.ts +50 -0
- package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
- package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
- package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
- package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
- package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
- package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
- package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
- package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
- package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/unit-worker-share.ts +1 -0
- package/scripts/time-runtime/time-background.ts +5 -1
- package/scripts/time-runtime/time-instant.ts +3 -1
- package/scripts/time-runtime/time-spans.ts +6 -139
- package/scripts/time-runtime/time-tool-call.ts +160 -0
- package/scripts/version/effective-upstream.ts +2 -20
- package/scripts/version/latest-corepack.ts +124 -93
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +6 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
- /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
- /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
- /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
- /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
- /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
- /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
- /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
- /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
- /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
- /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
- /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
- /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
- /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
- /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
- /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
- /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
- /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
2
|
-
import { run_ship_detach } from '#scripts/run/run-ship-detach'
|
|
3
|
-
import { run_ship_probe } from '#scripts/run/run-ship-probe'
|
|
2
|
+
import { run_ship_detach } from '#scripts/run/ship/run-ship-detach'
|
|
3
|
+
import { run_ship_probe } from '#scripts/run/ship/run-ship-probe'
|
|
4
4
|
|
|
5
5
|
const POLL_MS = 250
|
|
6
6
|
type ShipEnding = 'none' | 'success' | 'failed' | 'abnormal'
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { linkSync, statSync, unlinkSync } from 'node:fs'
|
|
3
|
+
import { process_identity } from '#scripts/josh/process-identity'
|
|
4
|
+
import { process_owner_schema } from '#scripts/josh/process-owner'
|
|
5
|
+
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
import { openai_lane_supervisor_decision } from './openai-lane-supervisor-decision'
|
|
8
|
+
|
|
9
|
+
// The supervisor's owner and state stamps — who holds a lane, and which child it last launched —
|
|
10
|
+
// split out of `openai-lane-supervisor.ts` when it reached its line limit (joshuafolkken/kit#3264).
|
|
11
|
+
// `openai_lane_supervisor` re-exports each public one under the name it always had, so the move
|
|
12
|
+
// changed no call site and no suite that spies on `openai_lane_supervisor`.
|
|
13
|
+
|
|
14
|
+
const OWNER_PREFIX = 'josh-openai-lane-supervisor-'
|
|
15
|
+
const STATE_PREFIX = 'josh-openai-lane-supervisor-state-'
|
|
16
|
+
const CLAIM_POLL_MS = 50
|
|
17
|
+
const CLAIM_POLL_LIMIT = 40
|
|
18
|
+
// The shared owner pair, with the pid narrowed: this record is written only by a live supervisor, so a
|
|
19
|
+
// pid that is not a real one marks it as corrupt rather than as a dead owner.
|
|
20
|
+
const owner_schema = process_owner_schema.extend({
|
|
21
|
+
issue: z.string(),
|
|
22
|
+
nonce: z.string(),
|
|
23
|
+
pid: z.number().int().positive(),
|
|
24
|
+
})
|
|
25
|
+
const state_schema = z.object({
|
|
26
|
+
owner_nonce: z.string(),
|
|
27
|
+
child_pid: z.number().int().positive().optional(),
|
|
28
|
+
child_process_start: z.string().optional(),
|
|
29
|
+
epoch: z.number().int().nonnegative(),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
type SupervisorOwner = z.infer<typeof owner_schema>
|
|
33
|
+
type SupervisorState = z.infer<typeof state_schema>
|
|
34
|
+
|
|
35
|
+
function marker_path(lane_directory: string): string {
|
|
36
|
+
return stamp_file.stamp_path(OWNER_PREFIX, lane_directory)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function state_path(lane_directory: string): string {
|
|
40
|
+
return stamp_file.stamp_path(STATE_PREFIX, lane_directory)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parsed_stamp<T>(target: string, schema: z.ZodType<T>): T | undefined {
|
|
44
|
+
try {
|
|
45
|
+
const value: unknown = JSON.parse(stamp_file.read_stamp_text(target) ?? '')
|
|
46
|
+
const parsed = schema.safeParse(value)
|
|
47
|
+
|
|
48
|
+
return parsed.success ? parsed.data : undefined
|
|
49
|
+
} catch {
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function read(lane_directory: string): SupervisorOwner | undefined {
|
|
55
|
+
return parsed_stamp(marker_path(lane_directory), owner_schema)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function read_state(lane_directory: string, nonce: string): SupervisorState | undefined {
|
|
59
|
+
const state = parsed_stamp(state_path(lane_directory), state_schema)
|
|
60
|
+
|
|
61
|
+
return state?.owner_nonce === nonce ? state : undefined
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function is_live(owner: SupervisorOwner): boolean {
|
|
65
|
+
return process_identity.is_same_process(owner.pid, owner.process_start) !== false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function active(lane_directory: string): SupervisorOwner | undefined {
|
|
69
|
+
const owner = read(lane_directory)
|
|
70
|
+
|
|
71
|
+
return owner !== undefined && is_live(owner) ? owner : undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function wait_for_active(lane_directory: string): Promise<SupervisorOwner | undefined> {
|
|
75
|
+
for (let attempt = 0; attempt < CLAIM_POLL_LIMIT; attempt += 1) {
|
|
76
|
+
const owner = active(lane_directory)
|
|
77
|
+
if (owner !== undefined) return owner
|
|
78
|
+
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_POLL_MS))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The hard link snapshots the exact owner inode. Several contenders may snapshot one stale owner,
|
|
86
|
+
// but only one can unlink it; a contender arriving after a replacement sees different inodes and
|
|
87
|
+
// cannot remove the new live owner.
|
|
88
|
+
function remove_if_unchanged(target: string): void {
|
|
89
|
+
const snapshot = `${target}.${String(process.pid)}.${randomUUID()}`
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
linkSync(target, snapshot)
|
|
93
|
+
const current = statSync(target)
|
|
94
|
+
const linked = statSync(snapshot)
|
|
95
|
+
if (current.dev === linked.dev && current.ino === linked.ino) unlinkSync(target)
|
|
96
|
+
} catch {
|
|
97
|
+
/* absence or replacement is resolved by the exclusive create below */
|
|
98
|
+
} finally {
|
|
99
|
+
stamp_file.remove_stamp(snapshot)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface ChildIdentity {
|
|
104
|
+
pid: number
|
|
105
|
+
process_start?: string
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function live_child(state: SupervisorState | undefined): ChildIdentity | undefined {
|
|
109
|
+
if (state?.child_pid === undefined) return undefined
|
|
110
|
+
const { child_pid, child_process_start } = state
|
|
111
|
+
|
|
112
|
+
if (process_identity.is_same_process(child_pid, child_process_start) === false) return undefined
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
pid: child_pid,
|
|
116
|
+
...(child_process_start !== undefined && { process_start: child_process_start }),
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function carried_child(
|
|
121
|
+
lane_directory: string,
|
|
122
|
+
owner: SupervisorOwner | undefined,
|
|
123
|
+
): ChildIdentity | undefined {
|
|
124
|
+
if (owner === undefined) return undefined
|
|
125
|
+
|
|
126
|
+
return live_child(read_state(lane_directory, owner.nonce))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function remove_stale_owner(target: string, previous: SupervisorOwner | undefined): boolean {
|
|
130
|
+
if (previous !== undefined && is_live(previous)) return false
|
|
131
|
+
if (stamp_file.read_stamp_text(target) !== undefined) remove_if_unchanged(target)
|
|
132
|
+
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function claim(issue: string, lane_directory: string, nonce: string): SupervisorOwner | undefined {
|
|
137
|
+
const target = marker_path(lane_directory)
|
|
138
|
+
const previous = read(lane_directory)
|
|
139
|
+
if (!remove_stale_owner(target, previous)) return undefined
|
|
140
|
+
const child = carried_child(lane_directory, previous)
|
|
141
|
+
const owner = { issue, nonce, ...process_identity.own_fields() }
|
|
142
|
+
if (!stamp_file.create_stamp(target, owner)) return undefined
|
|
143
|
+
stamp_file.replace_stamp(state_path(lane_directory), {
|
|
144
|
+
owner_nonce: nonce,
|
|
145
|
+
epoch: 0,
|
|
146
|
+
...(child !== undefined && {
|
|
147
|
+
child_pid: child.pid,
|
|
148
|
+
...(child.process_start !== undefined && {
|
|
149
|
+
child_process_start: child.process_start,
|
|
150
|
+
}),
|
|
151
|
+
}),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
return owner
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function update(lane_directory: string, owner: SupervisorOwner, state: SupervisorState): void {
|
|
158
|
+
if (read(lane_directory)?.nonce !== owner.nonce) return
|
|
159
|
+
stamp_file.replace_stamp(state_path(lane_directory), state)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function release(lane_directory: string, nonce: string): void {
|
|
163
|
+
if (read(lane_directory)?.nonce !== nonce) return
|
|
164
|
+
stamp_file.remove_stamp(state_path(lane_directory))
|
|
165
|
+
stamp_file.remove_stamp(marker_path(lane_directory))
|
|
166
|
+
openai_lane_supervisor_decision.remove(lane_directory, nonce)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const openai_lane_supervisor_owner = {
|
|
170
|
+
active,
|
|
171
|
+
claim,
|
|
172
|
+
marker_path,
|
|
173
|
+
read_state,
|
|
174
|
+
release,
|
|
175
|
+
state_path,
|
|
176
|
+
update,
|
|
177
|
+
wait_for_active,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { openai_lane_supervisor_owner, type SupervisorOwner }
|
|
@@ -1,180 +1,22 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto'
|
|
2
|
-
import { linkSync, statSync, unlinkSync } from 'node:fs'
|
|
3
2
|
import { fileURLToPath } from 'node:url'
|
|
4
3
|
import { agent_argv } from '#scripts/agent/agent-argv'
|
|
5
4
|
import type { AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
6
5
|
import { process_identity } from '#scripts/josh/process-identity'
|
|
7
|
-
import {
|
|
8
|
-
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import { run_cut, type RunCut } from '#scripts/run/cut/run-cut'
|
|
9
7
|
import { detached_launch, type LaunchArgv } from '#scripts/run/detached-launch'
|
|
10
|
-
import { run_cut, type RunCut } from '#scripts/run/run-cut'
|
|
11
|
-
import { z } from 'zod'
|
|
12
8
|
import { lane_child_invocation } from './lane-child-invocation'
|
|
13
9
|
import { lane_child_marker } from './lane-child-marker'
|
|
14
10
|
import { lane_dispatch_log } from './lane-dispatch-log'
|
|
15
11
|
import type { LaneInfo } from './lane-registry'
|
|
16
12
|
import { openai_lane_ship } from './openai-lane-ship'
|
|
17
13
|
import { openai_lane_supervisor_decision } from './openai-lane-supervisor-decision'
|
|
14
|
+
import { openai_lane_supervisor_owner, type SupervisorOwner } from './openai-lane-supervisor-owner'
|
|
18
15
|
import { openai_review_broker } from './openai-review-broker'
|
|
19
16
|
|
|
20
|
-
const
|
|
21
|
-
const STATE_PREFIX = 'josh-openai-lane-supervisor-state-'
|
|
17
|
+
const { claim, read_state, release, update } = openai_lane_supervisor_owner
|
|
22
18
|
const CHILD_POLL_MS = 250
|
|
23
|
-
const CLAIM_POLL_MS = 50
|
|
24
|
-
const CLAIM_POLL_LIMIT = 40
|
|
25
19
|
const SUPERVISOR_SCRIPT = fileURLToPath(new URL('openai-lane-supervisor-cli.ts', import.meta.url))
|
|
26
|
-
// The shared owner pair, with the pid narrowed: this record is written only by a live supervisor, so a
|
|
27
|
-
// pid that is not a real one marks it as corrupt rather than as a dead owner.
|
|
28
|
-
const owner_schema = process_owner_schema.extend({
|
|
29
|
-
issue: z.string(),
|
|
30
|
-
nonce: z.string(),
|
|
31
|
-
pid: z.number().int().positive(),
|
|
32
|
-
})
|
|
33
|
-
const state_schema = z.object({
|
|
34
|
-
owner_nonce: z.string(),
|
|
35
|
-
child_pid: z.number().int().positive().optional(),
|
|
36
|
-
child_process_start: z.string().optional(),
|
|
37
|
-
epoch: z.number().int().nonnegative(),
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
type SupervisorOwner = z.infer<typeof owner_schema>
|
|
41
|
-
type SupervisorState = z.infer<typeof state_schema>
|
|
42
|
-
|
|
43
|
-
function marker_path(lane_directory: string): string {
|
|
44
|
-
return stamp_file.stamp_path(OWNER_PREFIX, lane_directory)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function state_path(lane_directory: string): string {
|
|
48
|
-
return stamp_file.stamp_path(STATE_PREFIX, lane_directory)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function parsed_stamp<T>(target: string, schema: z.ZodType<T>): T | undefined {
|
|
52
|
-
try {
|
|
53
|
-
const value: unknown = JSON.parse(stamp_file.read_stamp_text(target) ?? '')
|
|
54
|
-
const parsed = schema.safeParse(value)
|
|
55
|
-
|
|
56
|
-
return parsed.success ? parsed.data : undefined
|
|
57
|
-
} catch {
|
|
58
|
-
return undefined
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function read(lane_directory: string): SupervisorOwner | undefined {
|
|
63
|
-
return parsed_stamp(marker_path(lane_directory), owner_schema)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function read_state(lane_directory: string, nonce: string): SupervisorState | undefined {
|
|
67
|
-
const state = parsed_stamp(state_path(lane_directory), state_schema)
|
|
68
|
-
|
|
69
|
-
return state?.owner_nonce === nonce ? state : undefined
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function is_live(owner: SupervisorOwner): boolean {
|
|
73
|
-
return process_identity.is_same_process(owner.pid, owner.process_start) !== false
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function active(lane_directory: string): SupervisorOwner | undefined {
|
|
77
|
-
const owner = read(lane_directory)
|
|
78
|
-
|
|
79
|
-
return owner !== undefined && is_live(owner) ? owner : undefined
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function wait_for_active(lane_directory: string): Promise<SupervisorOwner | undefined> {
|
|
83
|
-
for (let attempt = 0; attempt < CLAIM_POLL_LIMIT; attempt += 1) {
|
|
84
|
-
const owner = active(lane_directory)
|
|
85
|
-
if (owner !== undefined) return owner
|
|
86
|
-
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
87
|
-
await new Promise((resolve) => setTimeout(resolve, CLAIM_POLL_MS))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return undefined
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// The hard link snapshots the exact owner inode. Several contenders may snapshot one stale owner,
|
|
94
|
-
// but only one can unlink it; a contender arriving after a replacement sees different inodes and
|
|
95
|
-
// cannot remove the new live owner.
|
|
96
|
-
function remove_if_unchanged(target: string): void {
|
|
97
|
-
const snapshot = `${target}.${String(process.pid)}.${randomUUID()}`
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
linkSync(target, snapshot)
|
|
101
|
-
const current = statSync(target)
|
|
102
|
-
const linked = statSync(snapshot)
|
|
103
|
-
if (current.dev === linked.dev && current.ino === linked.ino) unlinkSync(target)
|
|
104
|
-
} catch {
|
|
105
|
-
/* absence or replacement is resolved by the exclusive create below */
|
|
106
|
-
} finally {
|
|
107
|
-
stamp_file.remove_stamp(snapshot)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
interface ChildIdentity {
|
|
112
|
-
pid: number
|
|
113
|
-
process_start?: string
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function live_child(state: SupervisorState | undefined): ChildIdentity | undefined {
|
|
117
|
-
if (state?.child_pid === undefined) return undefined
|
|
118
|
-
const { child_pid, child_process_start } = state
|
|
119
|
-
|
|
120
|
-
if (process_identity.is_same_process(child_pid, child_process_start) === false) {
|
|
121
|
-
return undefined
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
pid: child_pid,
|
|
126
|
-
...(child_process_start !== undefined && { process_start: child_process_start }),
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function carried_child(
|
|
131
|
-
lane_directory: string,
|
|
132
|
-
owner: SupervisorOwner | undefined,
|
|
133
|
-
): ChildIdentity | undefined {
|
|
134
|
-
if (owner === undefined) return undefined
|
|
135
|
-
|
|
136
|
-
return live_child(read_state(lane_directory, owner.nonce))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function remove_stale_owner(target: string, previous: SupervisorOwner | undefined): boolean {
|
|
140
|
-
if (previous !== undefined && is_live(previous)) return false
|
|
141
|
-
if (stamp_file.read_stamp_text(target) !== undefined) remove_if_unchanged(target)
|
|
142
|
-
|
|
143
|
-
return true
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function claim(issue: string, lane_directory: string, nonce: string): SupervisorOwner | undefined {
|
|
147
|
-
const target = marker_path(lane_directory)
|
|
148
|
-
const previous = read(lane_directory)
|
|
149
|
-
if (!remove_stale_owner(target, previous)) return undefined
|
|
150
|
-
const child = carried_child(lane_directory, previous)
|
|
151
|
-
const owner = { issue, nonce, ...process_identity.own_fields() }
|
|
152
|
-
if (!stamp_file.create_stamp(target, owner)) return undefined
|
|
153
|
-
stamp_file.replace_stamp(state_path(lane_directory), {
|
|
154
|
-
owner_nonce: nonce,
|
|
155
|
-
epoch: 0,
|
|
156
|
-
...(child !== undefined && {
|
|
157
|
-
child_pid: child.pid,
|
|
158
|
-
...(child.process_start !== undefined && {
|
|
159
|
-
child_process_start: child.process_start,
|
|
160
|
-
}),
|
|
161
|
-
}),
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
return owner
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function update(lane_directory: string, owner: SupervisorOwner, state: SupervisorState): void {
|
|
168
|
-
if (read(lane_directory)?.nonce !== owner.nonce) return
|
|
169
|
-
stamp_file.replace_stamp(state_path(lane_directory), state)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function release(lane_directory: string, nonce: string): void {
|
|
173
|
-
if (read(lane_directory)?.nonce !== nonce) return
|
|
174
|
-
stamp_file.remove_stamp(state_path(lane_directory))
|
|
175
|
-
stamp_file.remove_stamp(marker_path(lane_directory))
|
|
176
|
-
openai_lane_supervisor_decision.remove(lane_directory, nonce)
|
|
177
|
-
}
|
|
178
20
|
|
|
179
21
|
function handed_off(cut: RunCut | undefined, issue: string): boolean {
|
|
180
22
|
return (
|
|
@@ -350,17 +192,17 @@ function supervisor_argv(issue: string, nonce: string): LaunchArgv {
|
|
|
350
192
|
}
|
|
351
193
|
|
|
352
194
|
const openai_lane_supervisor = {
|
|
353
|
-
active,
|
|
195
|
+
active: openai_lane_supervisor_owner.active,
|
|
354
196
|
approve: openai_lane_supervisor_decision.approve,
|
|
355
197
|
cancel: openai_lane_supervisor_decision.cancel,
|
|
356
198
|
claim,
|
|
357
|
-
marker_path,
|
|
199
|
+
marker_path: openai_lane_supervisor_owner.marker_path,
|
|
358
200
|
new_nonce: randomUUID,
|
|
359
201
|
release,
|
|
360
|
-
state_path,
|
|
202
|
+
state_path: openai_lane_supervisor_owner.state_path,
|
|
361
203
|
supervise,
|
|
362
204
|
supervisor_argv,
|
|
363
|
-
wait_for_active,
|
|
205
|
+
wait_for_active: openai_lane_supervisor_owner.wait_for_active,
|
|
364
206
|
}
|
|
365
207
|
|
|
366
208
|
export { openai_lane_supervisor }
|
|
@@ -8,7 +8,7 @@ import { git_common_directory } from '#scripts/git/git-common-directory'
|
|
|
8
8
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
9
9
|
import { error_text } from '#scripts/lib/error-message'
|
|
10
10
|
import { detached_launch } from '#scripts/run/detached-launch'
|
|
11
|
-
import { run_ship_review } from '#scripts/run/run-ship-review'
|
|
11
|
+
import { run_ship_review } from '#scripts/run/ship/run-ship-review'
|
|
12
12
|
import { z } from 'zod'
|
|
13
13
|
import type { LaneInfo } from './lane-registry'
|
|
14
14
|
|
|
@@ -249,9 +249,7 @@ async function request(directory: string, issue: string, round: ReviewRound): Pr
|
|
|
249
249
|
if (target === undefined) return false
|
|
250
250
|
const response = read_record(target, response_schema)
|
|
251
251
|
|
|
252
|
-
if (response === undefined || !is_live_response(response, response.session))
|
|
253
|
-
return false
|
|
254
|
-
}
|
|
252
|
+
if (response === undefined || !is_live_response(response, response.session)) return false
|
|
255
253
|
|
|
256
254
|
const nonce = randomUUID()
|
|
257
255
|
const body: ReviewRequest = { session: response.session, nonce, round }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
// One parent-directory walk, in one place (joshuafolkken/kit#3260). Five modules had each written
|
|
4
|
+
// their own — the josh package-root lookup, the gate's project root, the effective-upstream version
|
|
5
|
+
// read, the local-bin shim search and the progress-interval read — and they had drifted to three
|
|
6
|
+
// different stop conditions. The walk stops at the filesystem root (where `dirname` answers itself)
|
|
7
|
+
// or at the first match, and is bounded so a path that never reaches a root — a broken mount, a
|
|
8
|
+
// mocked `dirname` — cannot spin.
|
|
9
|
+
const MAX_DEPTH = 64
|
|
10
|
+
|
|
11
|
+
// The directory itself and each of its ancestors up to the filesystem root, nearest-first.
|
|
12
|
+
function list(start_directory: string): ReadonlyArray<string> {
|
|
13
|
+
const directories: Array<string> = []
|
|
14
|
+
let current = path.resolve(start_directory)
|
|
15
|
+
|
|
16
|
+
while (directories.length < MAX_DEPTH && !directories.includes(current)) {
|
|
17
|
+
directories.push(current)
|
|
18
|
+
current = path.dirname(current)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return directories
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The nearest directory, the start included, that `is_match` accepts — undefined when the walk
|
|
25
|
+
// reaches the root without one.
|
|
26
|
+
function nearest(
|
|
27
|
+
start_directory: string,
|
|
28
|
+
is_match: (directory: string) => boolean,
|
|
29
|
+
): string | undefined {
|
|
30
|
+
return list(start_directory).find((directory) => is_match(directory))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ancestor_directories = { list, nearest }
|
|
34
|
+
|
|
35
|
+
export { ancestor_directories }
|
|
@@ -6,8 +6,8 @@ interface AnimationOptions<T> {
|
|
|
6
6
|
result_formatter?: (result: T) => string
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
function get_error_message(error_message: string | undefined): string {
|
|
10
|
-
return error_message
|
|
9
|
+
function get_error_message(error_message: string | undefined = 'Operation failed'): string {
|
|
10
|
+
return error_message
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function get_display_result<T>(result: T, formatter?: (result: T) => string): string {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execa } from 'execa'
|
|
2
|
+
import { SUITE_TIMEOUT_MS } from './timeouts'
|
|
2
3
|
|
|
3
4
|
// One definition of "run a check, keep its output, never throw".
|
|
4
5
|
//
|
|
@@ -12,11 +13,6 @@ import { execa } from 'execa'
|
|
|
12
13
|
const PNPM = 'pnpm'
|
|
13
14
|
const FORCE_COLOR = '1'
|
|
14
15
|
const FAIL_EXIT_CODE = 1
|
|
15
|
-
// Long enough for a cold unit suite on a large consumer, short enough that a check which never
|
|
16
|
-
// exits ends the run instead of holding it open with nothing printed — the same hazard
|
|
17
|
-
// `propagate-steps.ts` bounds with its own step timeout. Because output is buffered until the
|
|
18
|
-
// child finishes, a hung child shows nothing at all until this fires.
|
|
19
|
-
const PROCESS_TIMEOUT_MS = 1_800_000
|
|
20
16
|
|
|
21
17
|
interface BufferedProcessResult {
|
|
22
18
|
output: string
|
|
@@ -60,7 +56,7 @@ async function run_buffered_process(
|
|
|
60
56
|
all: true,
|
|
61
57
|
reject: false,
|
|
62
58
|
...(options.cwd !== undefined && { cwd: options.cwd }),
|
|
63
|
-
timeout: options.timeout_ms ??
|
|
59
|
+
timeout: options.timeout_ms ?? SUITE_TIMEOUT_MS,
|
|
64
60
|
})
|
|
65
61
|
|
|
66
62
|
return {
|
|
@@ -79,4 +75,4 @@ function is_process_failed(result: Pick<BufferedProcessResult, 'exit_code'>): bo
|
|
|
79
75
|
const buffered_process = { is_process_failed, run_buffered_process }
|
|
80
76
|
|
|
81
77
|
export type { BufferedProcessResult }
|
|
82
|
-
export { buffered_process, FAIL_EXIT_CODE
|
|
78
|
+
export { buffered_process, FAIL_EXIT_CODE }
|
package/scripts/lib/cli-flags.ts
CHANGED
|
@@ -61,6 +61,76 @@ function string_of(value: unknown): string | undefined {
|
|
|
61
61
|
return typeof value === 'string' ? value : undefined
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const
|
|
64
|
+
const LONG_FLAG_PREFIX = '--'
|
|
65
|
+
const SHORT_FLAG_PREFIX = '-'
|
|
66
|
+
|
|
67
|
+
// The key `parseArgs` files a `--long-flag` under.
|
|
68
|
+
function option_name(flag: string): string {
|
|
69
|
+
return flag.slice(LONG_FLAG_PREFIX.length)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// `parseArgs`'s own rule for a value it calls ambiguous: a dash and more. A lone `-` is a value — the
|
|
73
|
+
// stdin spelling `--body-file -` and `--decision-file -` rely on.
|
|
74
|
+
function is_unusable_value(value: string | boolean): boolean {
|
|
75
|
+
if (typeof value === 'boolean' || value.length === 0) return true
|
|
76
|
+
|
|
77
|
+
return value !== SHORT_FLAG_PREFIX && value.startsWith(SHORT_FLAG_PREFIX)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Every value one value-taking flag was given, `true` standing for one given none. Read leniently, so an
|
|
81
|
+
// unknown flag elsewhere on the line does not hide the answer: a strict read only answers `undefined`
|
|
82
|
+
// for the whole line, and this is what lets a command name the one flag that went wrong.
|
|
83
|
+
function given_values(argv: ReadonlyArray<string>, flag: string): ReadonlyArray<string | boolean> {
|
|
84
|
+
const name = option_name(flag)
|
|
85
|
+
const given = parse_or_undefined({
|
|
86
|
+
args: [...argv],
|
|
87
|
+
options: { [name]: { type: 'string', multiple: true } },
|
|
88
|
+
strict: false,
|
|
89
|
+
allowPositionals: true,
|
|
90
|
+
})?.values[name]
|
|
91
|
+
|
|
92
|
+
return Array.isArray(given) ? given : []
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Whether a value-taking flag was given without a usable value — last on the line, followed by
|
|
96
|
+
// another flag, or empty — so a command can say which value a shell ate rather than print its
|
|
97
|
+
// generic usage.
|
|
98
|
+
function is_value_unusable(argv: ReadonlyArray<string>, flag: string): boolean {
|
|
99
|
+
return given_values(argv, flag).some((value) => is_unusable_value(value))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function attach_one(attached: Array<string>, argument: string, flags: ReadonlyArray<string>): void {
|
|
103
|
+
const previous = attached.at(-1)
|
|
104
|
+
|
|
105
|
+
if (previous !== undefined && flags.includes(previous)) {
|
|
106
|
+
attached[attached.length - 1] = `${previous}=${argument}`
|
|
107
|
+
} else attached.push(argument)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Joins each listed free-text flag to the token after it (`--body -x` → `--body=-x`), so a value that
|
|
111
|
+
// opens with a dash — a Markdown bullet — reaches the command instead of being refused by `parseArgs` as
|
|
112
|
+
// ambiguous. Only the listed flags are joined: anywhere else a dash-led token is still the next flag.
|
|
113
|
+
function attach_values(
|
|
114
|
+
argv: ReadonlyArray<string>,
|
|
115
|
+
flags: ReadonlyArray<string>,
|
|
116
|
+
): ReadonlyArray<string> {
|
|
117
|
+
const attached: Array<string> = []
|
|
118
|
+
|
|
119
|
+
for (const argument of argv) attach_one(attached, argument, flags)
|
|
120
|
+
|
|
121
|
+
return attached
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const cli_flags = {
|
|
125
|
+
attach_values,
|
|
126
|
+
refuse_unknown_flags,
|
|
127
|
+
parse_or_undefined,
|
|
128
|
+
values_of,
|
|
129
|
+
arguments_of,
|
|
130
|
+
string_of,
|
|
131
|
+
option_name,
|
|
132
|
+
given_values,
|
|
133
|
+
is_value_unusable,
|
|
134
|
+
}
|
|
65
135
|
|
|
66
136
|
export { cli_flags }
|
|
@@ -26,13 +26,13 @@ function resolve_spawn_exit(executable: string, result: SpawnResult): number {
|
|
|
26
26
|
// covers for `exitCode`. Reading it through a signature that admits the real shape keeps the guard
|
|
27
27
|
// honest: a plain `result.stdout ?? ''` is reported as an unnecessary condition against the lying
|
|
28
28
|
// type, and dereferencing it unguarded crashes the caller.
|
|
29
|
-
function read_spawn_stdout(result: { stdout?: string }): string {
|
|
29
|
+
function read_spawn_stdout(result: { stdout?: string | undefined }): string {
|
|
30
30
|
return result.stdout ?? ''
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// Same declared-vs-actual gap as `read_spawn_stdout`, for the stream that carries git's and gh's
|
|
34
34
|
// failure messages.
|
|
35
|
-
function read_spawn_stderr(result: { stderr?: string }): string {
|
|
35
|
+
function read_spawn_stderr(result: { stderr?: string | undefined }): string {
|
|
36
36
|
return result.stderr ?? ''
|
|
37
37
|
}
|
|
38
38
|
|
package/scripts/lib/timeouts.ts
CHANGED
|
@@ -3,11 +3,44 @@
|
|
|
3
3
|
// limit stays stuck where it stopped and never comes back (joshuafolkken/kit#2981), so every such
|
|
4
4
|
// call takes one of these, and a value used in more than one place is named once here.
|
|
5
5
|
|
|
6
|
+
// A probe that only asks the local machine (`ps`, `lsof`, `git config`) on a path something else is
|
|
7
|
+
// waiting on. Failing answers "cannot tell", which every caller handles, so anything slower is a fault.
|
|
8
|
+
const PROBE_TIMEOUT_MS = 2000
|
|
6
9
|
// One HTTP request to a registry, an API or a webhook.
|
|
7
10
|
const FETCH_TIMEOUT_MS = 10_000
|
|
11
|
+
// One local `git` command. Generous for a `rev-parse`; a call that talks to a remote needs its own.
|
|
12
|
+
const GIT_TIMEOUT_MS = 10_000
|
|
13
|
+
// A hook's spawned formatter or spell check, which holds the edit's turn open while it runs. Read it
|
|
14
|
+
// against the worst-case run, not one spawn: an edit to a config input plans eslint, prettier and
|
|
15
|
+
// `eslint_d restart`, the first and last each with a second route behind the daemon — five spawns.
|
|
16
|
+
// The hook entry in `.claude/settings.json` declares 90 seconds, so five of these must fit under it,
|
|
17
|
+
// or the harness kill lands at a moment the hook did not choose — possibly inside `prettier --write`,
|
|
18
|
+
// which rewrites in place and can leave the file truncated. 15s × 5 = 75s. It is still two orders of
|
|
19
|
+
// magnitude beyond the 0.84s a warm run takes, so reaching it means something is already wrong.
|
|
20
|
+
const HOOK_PROCESS_TIMEOUT_MS = 15_000
|
|
21
|
+
// One `gh api` read whose answer only shapes a report or a classification.
|
|
22
|
+
const GH_API_TIMEOUT_MS = 20_000
|
|
8
23
|
// A short command that only answers a question (`gh --version`, `pnpm config get`).
|
|
9
24
|
const COMMAND_TIMEOUT_MS = 30_000
|
|
10
|
-
// A
|
|
25
|
+
// A type-aware ESLint run over a few files. A cold one is seconds; this is two orders beyond that.
|
|
26
|
+
const LINT_TIMEOUT_MS = 180_000
|
|
27
|
+
// A `pnpm install` or a global install, which can download the whole dependency tree on a cold
|
|
28
|
+
// store. Ten minutes covers that fetch and still ends the command when a registry never answers. It
|
|
29
|
+
// is well under `SUITE_TIMEOUT_MS` because it blocks a command a person is waiting on.
|
|
11
30
|
const INSTALL_TIMEOUT_MS = 600_000
|
|
31
|
+
// A check or step that can run a consumer's full unit suite or a `pnpm add` — long enough for a cold
|
|
32
|
+
// suite on a large consumer, short enough that a hung child ends the run instead of holding it open
|
|
33
|
+
// with nothing printed (buffered output shows nothing at all until this fires).
|
|
34
|
+
const SUITE_TIMEOUT_MS = 1_800_000
|
|
12
35
|
|
|
13
|
-
export {
|
|
36
|
+
export {
|
|
37
|
+
COMMAND_TIMEOUT_MS,
|
|
38
|
+
FETCH_TIMEOUT_MS,
|
|
39
|
+
GH_API_TIMEOUT_MS,
|
|
40
|
+
GIT_TIMEOUT_MS,
|
|
41
|
+
HOOK_PROCESS_TIMEOUT_MS,
|
|
42
|
+
INSTALL_TIMEOUT_MS,
|
|
43
|
+
LINT_TIMEOUT_MS,
|
|
44
|
+
PROBE_TIMEOUT_MS,
|
|
45
|
+
SUITE_TIMEOUT_MS,
|
|
46
|
+
}
|
|
@@ -3,6 +3,7 @@ import { statSync } from 'node:fs'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { find_local_bin_upwards } from '#scripts/build/local-bin'
|
|
5
5
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import { LINT_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
6
7
|
import { execa } from 'execa'
|
|
7
8
|
import { z } from 'zod'
|
|
8
9
|
import { effective_limit, type LineRuleOptions } from './effective-limit'
|
|
@@ -95,9 +96,6 @@ const RULE_FLAG = '--rule'
|
|
|
95
96
|
const CACHE_PREFIX = 'josh-lines-eslint-cache-'
|
|
96
97
|
const CACHE_LOCATION_FLAG = '--cache-location'
|
|
97
98
|
const CACHE_KEY_LENGTH = 8
|
|
98
|
-
// Bounded so a hung child ends the call rather than holding it open. A cold type-aware lint of a few
|
|
99
|
-
// files is seconds; this is two orders of magnitude beyond that.
|
|
100
|
-
const PROCESS_TIMEOUT_MS = 180_000
|
|
101
99
|
|
|
102
100
|
// Only the two fields this reads. `ruleId` is nullable on a parse error or an ignore warning, and
|
|
103
101
|
// both of those are answers — "no count for this path" — rather than failures.
|
|
@@ -200,7 +198,7 @@ async function run_probe(group: ProbeGroup, project_root: string): Promise<strin
|
|
|
200
198
|
reject: false,
|
|
201
199
|
stdout: 'pipe',
|
|
202
200
|
stderr: 'ignore',
|
|
203
|
-
timeout:
|
|
201
|
+
timeout: LINT_TIMEOUT_MS,
|
|
204
202
|
})
|
|
205
203
|
|
|
206
204
|
return result.stdout
|
|
@@ -239,9 +237,9 @@ function count_or_zero(
|
|
|
239
237
|
|
|
240
238
|
// Keyed by the absolute path eslint reports, so the caller's spelling of a path does not have to
|
|
241
239
|
// match it. A path eslint refused is simply absent, and the caller reports that rather than a number.
|
|
242
|
-
function counts_in(raw_output: string | undefined): ReadonlyMap<string, number> {
|
|
240
|
+
function counts_in(raw_output: string | undefined = 'null'): ReadonlyMap<string, number> {
|
|
243
241
|
const counts = new Map<string, number>()
|
|
244
|
-
const parsed = results_schema.safeParse(JSON.parse(raw_output
|
|
242
|
+
const parsed = results_schema.safeParse(JSON.parse(raw_output))
|
|
245
243
|
|
|
246
244
|
if (!parsed.success) return counts
|
|
247
245
|
|