@joshuafolkken/kit 1.1514.0 → 1.1784.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/investigator.md +1 -1
- package/.claude/settings.json +5 -5
- package/.claude/skills/epic-commands/SKILL.md +26 -65
- package/.claude/skills/epic-commands/epic-bundle.md +3 -1
- package/.claude/skills/workflow-commands/SKILL.md +5 -5
- package/.claude/skills/workflow-commands/background-commands.md +5 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +10 -8
- package/.claude/skills/workflow-commands/backlogrun-handoff-report.md +7 -25
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +17 -13
- package/.claude/skills/workflow-commands/backlogrun-park.md +14 -23
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -2
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +5 -5
- package/.claude/skills/workflow-commands/backlogrun-steps.md +10 -10
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +13 -7
- package/.claude/skills/workflow-commands/delegation.md +6 -5
- package/.claude/skills/workflow-commands/entry-sequence.md +9 -9
- package/.claude/skills/workflow-commands/followup.md +9 -31
- package/.claude/skills/workflow-commands/fullrun-steps.md +36 -45
- package/.claude/skills/workflow-commands/fullrun.md +2 -2
- package/.claude/skills/workflow-commands/halfrun.md +17 -25
- package/.claude/skills/workflow-commands/issue-comments.md +16 -12
- package/.claude/skills/workflow-commands/issue-scout.md +2 -2
- package/.claude/skills/workflow-commands/kickoff.md +24 -23
- package/.claude/skills/workflow-commands/latest-gate.md +7 -12
- package/.claude/skills/workflow-commands/needs-human-review.md +9 -14
- package/.claude/skills/workflow-commands/observation-filing.md +4 -36
- package/.claude/skills/workflow-commands/observation-ledger.md +1 -1
- package/.claude/skills/workflow-commands/pre-gate-cut.md +13 -30
- package/.claude/skills/workflow-commands/prerequisite.md +10 -16
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/prrun.md +3 -7
- package/.claude/skills/workflow-commands/retrospective.md +1 -1
- package/.claude/skills/workflow-commands/split-assessment.md +13 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +9 -9
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-node/action.yml +9 -0
- package/.github/actions/setup-pnpm/action.yml +1 -1
- package/.github/dependabot.yml +16 -0
- package/.github/release.yml +0 -2
- package/.github/workflows/ci.yml +120 -10
- package/.github/workflows/publish.yml +8 -1
- package/.github/workflows/sonar-qube.yml +1 -0
- package/.prettierignore +2 -0
- package/.vscode/tasks.json +1 -2
- package/CLAUDE.md +3 -7
- package/README.md +4 -2
- package/SECURITY.md +1 -1
- package/dist/CLAUDE.md +3 -7
- package/dist/commands/document-read-cli.js +882 -0
- package/dist/commands/{document-section-cli.js → document-section-main.js} +79 -37
- package/dist/commands/read-set-cli.js +1867 -0
- package/dist/commands/run-prep-main.js +21653 -0
- package/dist/commands/run-step-cli.js +23880 -0
- package/dist/config-merge/index.js +5 -1
- package/dist/hooks/audit-provision.js +20 -35
- package/dist/hooks/{chunk-DGT54IH5.js → chunk-AZX4FGQ3.js} +63 -17
- package/dist/hooks/chunk-CWMBGKYW.js +6483 -0
- package/dist/hooks/chunk-O2YBIDSI.js +4306 -0
- package/dist/hooks/{chunk-TNUMY6NL.js → chunk-PT5R6UJD.js} +4193 -1900
- package/dist/hooks/chunk-TUIFS5E7.js +72 -0
- package/dist/hooks/{chunk-723HKZO5.js → chunk-VWWCFGWK.js} +1 -1
- package/dist/hooks/{chunk-APUO6U4J.js → chunk-WR2KQAYZ.js} +1843 -440
- package/dist/hooks/{chunk-O6GDRNJB.js → chunk-XGBNSWEI.js} +248 -46
- package/dist/hooks/chunk-YCDOIR3I.js +3456 -0
- package/dist/hooks/codex-hook-adapter.js +10 -22
- package/dist/hooks/format-edited.js +5 -6
- package/dist/hooks/inputs.json +443 -317
- package/dist/hooks/pretool-guard.js +7 -25
- package/dist/hooks/session-lang.js +44 -51
- package/dist/hooks/stop-guard.js +7716 -2187
- package/dist/josh.js +6002 -3583
- package/dist/self-sync-guard/index.js +74 -10
- package/dist/version/index.js +115 -406
- package/eslint/base.js +1 -1
- package/lefthook/base.yml +3 -1
- package/package.json +10 -48
- package/pnpm-workspace.yaml +4 -2
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +1 -7
- package/prompts/collaboration-workflow/issue-citation.md +1 -1
- package/prompts/collaboration-workflow/issue-template.md +33 -16
- package/prompts/collaboration-workflow/lane-instructions.md +1 -2
- package/prompts/collaboration-workflow/measurement.md +37 -0
- package/prompts/collaboration-workflow/operating-rules.md +4 -6
- package/prompts/collaboration-workflow/output-bounds.md +2 -7
- package/prompts/collaboration-workflow/plan-comment.md +3 -42
- package/prompts/collaboration-workflow/principles.md +5 -0
- package/prompts/collaboration-workflow/report-format.md +31 -60
- package/prompts/collaboration-workflow/residency.md +2 -2
- package/prompts/collaboration-workflow/shell-body.md +6 -16
- package/prompts/collaboration-workflow/turn-batching.md +0 -2
- package/prompts/collaboration-workflow/upstream-interrupt.md +14 -4
- package/prompts/collaboration-workflow/wip-cap.md +1 -1
- package/prompts/collaboration-workflow.md +3 -7
- package/prompts/refactoring.md +1 -1
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +41 -21
- package/prompts/testing-guide.md +8 -43
- package/scripts/agent/agent-event.ts +15 -15
- package/scripts/agent/agent-exit-record.ts +13 -1
- package/scripts/agent/api-auth.ts +41 -0
- package/scripts/agent/api-outage.ts +1 -1
- package/scripts/auto-ok/auto-ok-cli.ts +14 -16
- package/scripts/auto-ok/auto-ok-judge-read.ts +12 -3
- package/scripts/auto-ok/auto-ok-judge-write.ts +46 -19
- package/scripts/auto-ok/auto-ok-judge.ts +13 -2
- package/scripts/auto-ok/auto-ok-main.ts +17 -0
- package/scripts/backlog/backlog-arrival.ts +14 -3
- package/scripts/backlog/backlog-budget-cli.ts +7 -33
- package/scripts/backlog/backlog-budget-main.ts +16 -0
- package/scripts/backlog/backlog-budget.ts +2 -3
- package/scripts/backlog/backlog-defect-priority.ts +4 -3
- package/scripts/backlog/backlog-drive-adopt.ts +67 -0
- package/scripts/backlog/backlog-drive-args.ts +4 -5
- package/scripts/backlog/backlog-drive-cli.ts +113 -28
- package/scripts/backlog/backlog-drive-hold.ts +162 -0
- package/scripts/backlog/backlog-drive-idle.ts +151 -0
- package/scripts/backlog/backlog-drive-judge.ts +25 -0
- package/scripts/backlog/backlog-drive-memory.ts +126 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +5 -10
- package/scripts/backlog/backlog-drive-named.ts +53 -6
- package/scripts/backlog/backlog-drive-offer-argv.ts +3 -8
- package/scripts/backlog/backlog-drive-paused.ts +169 -0
- package/scripts/backlog/backlog-drive-restore.ts +30 -7
- package/scripts/backlog/backlog-drive-seats.ts +18 -8
- package/scripts/backlog/backlog-drive-token-handling.ts +38 -0
- package/scripts/backlog/backlog-drive.ts +79 -75
- package/scripts/backlog/backlog-next-main.ts +16 -0
- package/scripts/backlog/backlog-next-shared.ts +73 -0
- package/scripts/backlog/backlog-next.ts +48 -16
- package/scripts/backlog/backlog-occupied.ts +52 -39
- package/scripts/backlog/backlog-offer-cli.ts +6 -21
- package/scripts/backlog/backlog-offer-exclude.ts +15 -0
- package/scripts/backlog/backlog-offer-main.ts +12 -0
- package/scripts/backlog/backlog-offer.ts +36 -3
- package/scripts/backlog/backlog-overlap.ts +22 -4
- package/scripts/backlog/backlog-plan-read.ts +6 -2
- package/scripts/backlog/backlog-plan.ts +18 -4
- package/scripts/backlog/backlog-pool.ts +10 -10
- package/scripts/backlog/backlog-rank.ts +10 -2
- package/scripts/backlog/backlog-ready.ts +112 -30
- package/scripts/backlog/backlog-stalled-detect.ts +109 -36
- package/scripts/backlog/backlog-stalled.ts +52 -16
- package/scripts/backlog/backlog-wait-cause.ts +86 -0
- package/scripts/backlog/backlog-waves.ts +39 -13
- package/scripts/backlog/backlogrun-args.ts +19 -7
- package/scripts/backlog/backlogrun-cli.ts +19 -5
- package/scripts/backlog/backlogrun-launch.ts +6 -0
- package/scripts/backlog/backlogrun-restart.ts +97 -0
- package/scripts/backlog/backlogrun-stop.ts +79 -7
- package/scripts/behavior/behavior-cli.ts +2 -2
- package/scripts/behavior/behavior-rules.ts +47 -24
- package/scripts/build/build-bin.ts +2 -16
- package/scripts/build/build-claude-md.ts +0 -7
- package/scripts/build/build-commands.ts +0 -13
- package/scripts/build/build-hooks.ts +49 -48
- package/scripts/build/hook-bundle-stamp.ts +29 -11
- package/scripts/build/local-bin.ts +1 -1
- package/scripts/cases/cases-cli.ts +25 -13
- package/scripts/cases/cases-interaction-scan.ts +134 -0
- package/scripts/cases/cases-interaction.ts +183 -0
- package/scripts/cases/cases-logic.ts +39 -6
- package/scripts/ci/pr-classification-main.ts +18 -0
- package/scripts/ci/pr-classification.ts +10 -17
- package/scripts/clone/clone-scan.ts +1 -1
- package/scripts/cost-runtime/cost-blocks.ts +22 -37
- package/scripts/cost-runtime/cost-cli.ts +31 -14
- package/scripts/cost-runtime/cost-main.ts +12 -0
- package/scripts/cost-runtime/cost-transcript.ts +1 -0
- package/scripts/delegation/delegation-policy.ts +17 -4
- package/scripts/delegation/duplicate-read-guard.ts +0 -8
- package/scripts/delegation/investigation-guard.ts +0 -8
- package/scripts/delegation/investigation-reads.ts +3 -4
- package/scripts/dependabot/dependabot-cooldown.ts +194 -0
- package/scripts/discovery/repo-discovery.ts +24 -6
- package/scripts/doctor/doctor-consumer.ts +2 -5
- package/scripts/document/document-byte-budget.ts +8 -6
- package/scripts/document/document-scan.ts +1 -1
- package/scripts/document/document-section-cli.ts +0 -8
- package/scripts/document/document-section-main.ts +12 -0
- package/scripts/epic/epic-audit-cli.ts +12 -6
- package/scripts/epic/epic-bundle-apply.ts +137 -0
- package/scripts/epic/epic-bundle-cli.ts +32 -121
- package/scripts/epic/epic-bundle-format.ts +116 -0
- package/scripts/epic/epic-bundle-main.ts +17 -0
- package/scripts/epic/epic-busy.ts +5 -12
- package/scripts/epic/epic-classify.ts +24 -15
- package/scripts/epic/epic-cli-argv.ts +31 -0
- package/scripts/epic/epic-cli-decide.ts +42 -0
- package/scripts/epic/epic-cli.ts +6 -38
- package/scripts/epic/epic-close.ts +112 -55
- package/scripts/epic/epic-cross-repo.ts +4 -7
- package/scripts/epic/epic-decide.ts +119 -0
- package/scripts/epic/epic-decision.ts +9 -0
- package/scripts/epic/epic-fetch.ts +4 -0
- package/scripts/epic/epic-graph.ts +2 -0
- package/scripts/epic/epic-issue.ts +5 -1
- package/scripts/epic/epic-label-age.ts +1 -1
- package/scripts/epic/epic-lane-offer.ts +39 -4
- package/scripts/epic/epic-next-main.ts +24 -0
- package/scripts/epic/epic-next.ts +6 -24
- package/scripts/epic/epic-outside-blocker.ts +6 -2
- package/scripts/epic/epic-parse.ts +9 -3
- package/scripts/epic/epic-read.ts +1 -0
- package/scripts/epic/epic-reconcile.ts +39 -0
- package/scripts/epic/epic-run.ts +10 -1
- package/scripts/epic/epic-solo.ts +50 -14
- package/scripts/epic/epic.ts +41 -7
- package/scripts/exports/unused-members-cli.ts +2 -2
- package/scripts/followup/followup-checks-event.ts +8 -3
- package/scripts/followup/followup-coderabbit-wait.ts +82 -0
- package/scripts/followup/followup-review-gates.ts +81 -0
- package/scripts/followup/git-followup-stages.ts +13 -3
- package/scripts/followup/git-followup-workflow.ts +19 -66
- package/scripts/followup/git-pr-followup-wrapup.ts +9 -14
- package/scripts/followup/git-pr-followup.ts +36 -8
- package/scripts/fresh-machine/fresh-machine-cli.ts +1 -1
- package/scripts/fresh-machine/fresh-machine-run.ts +8 -8
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +1 -1
- package/scripts/gate/busy-average.ts +58 -0
- package/scripts/gate/check-cpu.ts +60 -0
- package/scripts/gate/check-inputs.ts +39 -95
- package/scripts/gate/check-reuse.ts +33 -54
- package/scripts/gate/command-memory.ts +192 -0
- package/scripts/gate/core-admission.ts +218 -28
- package/scripts/gate/core-budget-timing.ts +37 -0
- package/scripts/gate/core-budget.ts +80 -98
- package/scripts/gate/core-wait-record.ts +48 -0
- package/scripts/gate/core-wait.ts +37 -5
- package/scripts/gate/core-weights.ts +86 -0
- package/scripts/gate/free-cores.ts +27 -4
- package/scripts/gate/gate-ledger.ts +32 -4
- package/scripts/gate/gate-plan.ts +41 -34
- package/scripts/gate/gate-report.ts +14 -2
- package/scripts/gate/gate-run-steps.ts +53 -8
- package/scripts/gate/gate-skip.ts +12 -2
- package/scripts/gate/gate-steps.ts +12 -1
- package/scripts/gate/input-fingerprint.ts +42 -0
- package/scripts/gate/machine-capacity.ts +65 -31
- package/scripts/gate/moved-paths.ts +67 -0
- package/scripts/gate/project-checks.ts +5 -12
- package/scripts/gate/scoped-green.ts +12 -8
- package/scripts/gate/since-green.ts +62 -0
- package/scripts/gate/toolchain-inputs.ts +115 -0
- package/scripts/gate/verification-gate.ts +27 -24
- package/scripts/gate/verification-inputs.ts +250 -0
- package/scripts/gh/gh-author-association.ts +96 -9
- package/scripts/gh/git-ai-review-scan.ts +4 -2
- package/scripts/gh/git-gh-api-path.ts +15 -0
- package/scripts/gh/git-gh-branch-rules.ts +73 -0
- package/scripts/gh/git-gh-conditional.ts +5 -2
- package/scripts/gh/git-gh-issue-read.ts +5 -1
- package/scripts/gh/git-gh-issue-write.ts +67 -29
- package/scripts/gh/git-gh-issue.ts +7 -3
- package/scripts/gh/git-gh-poll-read.ts +89 -0
- package/scripts/gh/git-gh-pr-auto-merge.ts +8 -3
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +33 -20
- package/scripts/gh/git-gh-pr.ts +27 -15
- package/scripts/gh/git-pr-checks-eval.ts +39 -7
- package/scripts/gh/git-pr-checks-parse.ts +39 -9
- package/scripts/gh/git-pr-merge-checked.ts +80 -0
- package/scripts/git/base-tree.ts +19 -2
- package/scripts/git/changed-file-scope.ts +6 -4
- package/scripts/git/changed-paths.ts +18 -1
- package/scripts/git/git-diff-reads.ts +27 -2
- package/scripts/git/git-grep-hits.ts +85 -0
- package/scripts/git/git-location-environment.ts +1 -1
- package/scripts/git/git-merge-conflicts.ts +83 -0
- package/scripts/git/git-preflight.ts +37 -12
- package/scripts/git/git-push-transport.ts +1 -2
- package/scripts/git/git-schemas.ts +17 -7
- package/scripts/git/git-spawn.ts +1 -2
- package/scripts/git/git-status.ts +4 -4
- package/scripts/git/main-merge-main.ts +12 -0
- package/scripts/git/main-merge-regenerate.ts +2 -2
- package/scripts/git/main-merge.ts +25 -9
- package/scripts/git/main-sync-main.ts +12 -0
- package/scripts/git/main-sync.ts +0 -9
- package/scripts/git/stash/git-stash.ts +27 -4
- package/scripts/git/stash/{stash-pop-args.ts → stash-args.ts} +13 -10
- package/scripts/git/stash/stash-pop-cli.ts +6 -12
- package/scripts/git/stash/stash-pop-main.ts +12 -0
- package/scripts/git/stash/stash-push-cli.ts +76 -0
- package/scripts/git/stash/stash-push-main.ts +12 -0
- package/scripts/hooks/batch-guard.ts +0 -8
- package/scripts/hooks/check-commit-message-main.ts +8 -0
- package/scripts/hooks/check-commit-message.ts +4 -7
- package/scripts/hooks/format-edited-file.ts +8 -24
- package/scripts/hooks/pre-commit-lint.ts +30 -3
- package/scripts/hooks/pre-push-unit.ts +4 -2
- package/scripts/hooks/pretool-guard-cli.ts +1 -8
- package/scripts/hooks/pretool-guard.ts +32 -19
- package/scripts/hooks/reserved-run.ts +67 -9
- package/scripts/hooks/run-hook.sh +13 -4
- package/scripts/hooks/shell-rules-notice.ts +37 -0
- package/scripts/hooks/stop-actions.ts +2 -21
- package/scripts/hooks/stop-guard.ts +13 -9
- package/scripts/init/hook-command-rewrite.ts +19 -5
- package/scripts/init/hook-launch.ts +15 -17
- package/scripts/init/init-actions.ts +0 -6
- package/scripts/init/init-ai-copy.ts +10 -36
- package/scripts/init/init-bootstrap.ts +9 -8
- package/scripts/init/init-dry-run-report.ts +1 -1
- package/scripts/init/init-dry-run.ts +1 -1
- package/scripts/init/init-file-action.ts +7 -9
- package/scripts/init/init-logic-json-merge.ts +2 -2
- package/scripts/init/init-logic-migrate.ts +9 -82
- package/scripts/init/init-logic.ts +3 -2
- package/scripts/init/init-main.ts +12 -0
- package/scripts/init/init.ts +5 -12
- package/scripts/init/kit-development-versions.ts +3 -7
- package/scripts/init/kit-written-paths.ts +2 -2
- package/scripts/init/project-profile.ts +5 -4
- package/scripts/init/tsconfig-preset-migration.ts +2 -1
- package/scripts/init/vscode-tasks.ts +1 -1
- package/scripts/issue/defect-origin-cli.ts +106 -0
- package/scripts/issue/defect-origin-line.ts +60 -0
- package/scripts/issue/defect-origin-main.ts +11 -0
- package/scripts/issue/defect-origin.ts +114 -0
- package/scripts/issue/defect-rate-cli.ts +34 -78
- package/scripts/issue/defect-rate.ts +0 -16
- package/scripts/issue/defect-report-cli.ts +128 -0
- package/scripts/issue/defect-report-format.ts +56 -0
- package/scripts/issue/defect-report-main.ts +11 -0
- package/scripts/issue/defect-report.ts +160 -0
- package/scripts/issue/expected-effect-lint.ts +1 -0
- package/scripts/issue/issue-already-done-cli.ts +62 -0
- package/scripts/issue/issue-body-or-comment.ts +40 -0
- package/scripts/issue/issue-cite-cli.ts +0 -9
- package/scripts/issue/issue-cite-main.ts +12 -0
- package/scripts/issue/issue-classification.ts +12 -7
- package/scripts/issue/issue-comment-cli.ts +10 -24
- package/scripts/issue/issue-comment-main.ts +12 -0
- package/scripts/issue/issue-evidence-exit.ts +100 -0
- package/scripts/issue/issue-file-cli.ts +29 -7
- package/scripts/issue/issue-file.ts +19 -19
- package/scripts/issue/issue-fold-cli.ts +2 -11
- package/scripts/issue/issue-fold-main.ts +12 -0
- package/scripts/issue/issue-labels.ts +81 -90
- package/scripts/issue/issue-lint-cli.ts +2 -10
- package/scripts/issue/issue-lint-main.ts +12 -0
- package/scripts/issue/issue-measured-cli.ts +56 -0
- package/scripts/issue/issue-merged.ts +19 -2
- package/scripts/issue/issue-not-before-label.ts +135 -0
- package/scripts/issue/issue-not-before.ts +111 -0
- package/scripts/issue/issue-number-shape.ts +15 -1
- package/scripts/issue/issue-park.ts +28 -0
- package/scripts/issue/issue-plan-cli.ts +151 -0
- package/scripts/issue/issue-priority.ts +151 -0
- package/scripts/issue/issue-rank.ts +7 -1
- package/scripts/issue/issue-read-cli.ts +3 -10
- package/scripts/issue/issue-read-main.ts +12 -0
- package/scripts/issue/issue-read.ts +29 -5
- package/scripts/issue/issue-release-cli.ts +0 -10
- package/scripts/issue/issue-release-main.ts +13 -0
- package/scripts/issue/issue-release.ts +7 -12
- package/scripts/issue/issue-sample-gate.ts +192 -0
- package/scripts/issue/issue-sample-source.ts +76 -0
- package/scripts/issue/issue-scout-cli.ts +0 -11
- package/scripts/issue/issue-scout-main.ts +14 -0
- package/scripts/issue/issue-state-cli.ts +0 -10
- package/scripts/issue/issue-state-main.ts +12 -0
- package/scripts/issue/issue-watch-diff.ts +56 -15
- package/scripts/issue/issue-watch.ts +2 -1
- package/scripts/issue/issue-wip.ts +2 -7
- package/scripts/issue/measure-rerun-cli.ts +13 -8
- package/scripts/issue/plan-consumers-cli.ts +39 -0
- package/scripts/issue/plan-consumers-main.ts +11 -0
- package/scripts/issue/plan-consumers-section.ts +55 -0
- package/scripts/issue/plan-consumers.ts +127 -0
- package/scripts/josh/agent-session-environment.ts +1 -1
- package/scripts/josh/command-usage.ts +15 -43
- package/scripts/josh/file-map-stamp.ts +49 -16
- package/scripts/josh/hook-decision.ts +32 -20
- package/scripts/josh/josh-command-map.ts +4 -1
- package/scripts/josh/josh-command-types.ts +12 -6
- package/scripts/josh/josh-commands-agent-hooks.ts +83 -0
- package/scripts/josh/josh-commands-ai.ts +157 -158
- package/scripts/josh/josh-commands-backlog.ts +82 -16
- package/scripts/josh/josh-commands-clone.ts +1 -0
- package/scripts/josh/josh-commands-development.ts +111 -71
- package/scripts/josh/josh-commands-document.ts +43 -1
- package/scripts/josh/josh-commands-epic.ts +171 -0
- package/scripts/josh/josh-commands-hooks.ts +25 -2
- package/scripts/josh/josh-commands-issue-write.ts +181 -0
- package/scripts/josh/josh-commands-issue.ts +124 -24
- package/scripts/josh/josh-commands-lane.ts +103 -2
- package/scripts/josh/josh-commands-lint.ts +19 -8
- package/scripts/josh/josh-commands-maintenance.ts +112 -4
- package/scripts/josh/josh-commands-pause.ts +29 -0
- package/scripts/josh/josh-commands-progress.ts +87 -0
- package/scripts/josh/josh-commands-project.ts +138 -9
- package/scripts/josh/josh-commands-review.ts +124 -0
- package/scripts/josh/josh-commands-run.ts +211 -23
- package/scripts/josh/josh-commands-ship.ts +169 -0
- package/scripts/josh/josh-commands-split.ts +9 -0
- package/scripts/josh/josh-commands-versioning.ts +49 -2
- package/scripts/josh/josh-commands-workflow.ts +144 -6
- package/scripts/josh/josh-logic.ts +6 -13
- package/scripts/josh/package-directory.ts +2 -4
- package/scripts/josh/process-identity.ts +13 -4
- package/scripts/josh/session-language-cli.ts +4 -8
- package/scripts/josh/session-language-main.ts +13 -0
- package/scripts/josh/session-language.ts +6 -1
- package/scripts/josh/shared-cache.ts +90 -0
- package/scripts/josh/stamp-file.ts +9 -15
- package/scripts/lane/lane-await.ts +51 -12
- package/scripts/lane/lane-cache.ts +5 -30
- package/scripts/lane/lane-capacity.ts +14 -23
- package/scripts/lane/lane-check-stats.ts +62 -21
- package/scripts/lane/lane-child-invocation.ts +8 -0
- package/scripts/lane/lane-cli.ts +15 -5
- package/scripts/lane/lane-close.ts +86 -18
- package/scripts/lane/lane-core-wait-stats.ts +66 -0
- package/scripts/lane/lane-dispatch.ts +24 -3
- package/scripts/lane/lane-gap-stats.ts +156 -0
- package/scripts/lane/lane-handoff.ts +69 -1
- package/scripts/lane/lane-held-stats.ts +129 -0
- package/scripts/lane/lane-install.ts +4 -11
- package/scripts/lane/lane-launch-cli.ts +0 -9
- package/scripts/lane/lane-launch-main.ts +12 -0
- package/scripts/lane/lane-ledger-schema.ts +266 -0
- package/scripts/lane/lane-ledger.ts +92 -123
- package/scripts/lane/lane-limit-cli.ts +12 -1
- package/scripts/lane/lane-measure-cli.ts +57 -11
- package/scripts/lane/lane-open.ts +9 -1
- package/scripts/lane/lane-parked.ts +3 -3
- package/scripts/lane/lane-pause-cli.ts +130 -0
- package/scripts/lane/lane-pause.ts +158 -0
- package/scripts/lane/lane-process-table.ts +166 -0
- package/scripts/lane/lane-reap-settle.ts +124 -0
- package/scripts/lane/lane-reap.ts +50 -15
- package/scripts/lane/lane-registry.ts +24 -3
- package/scripts/lane/lane-residents.ts +162 -0
- package/scripts/lane/lane-restart.ts +22 -2
- package/scripts/lane/lane-round-two-stats.ts +49 -0
- package/scripts/lane/lane-sampler.ts +4 -0
- package/scripts/lane/lane-session-action.ts +19 -5
- package/scripts/lane/lane-session-broker.ts +102 -21
- package/scripts/lane/lane-snapshot-trace.ts +84 -0
- package/scripts/lane/lane-spans.ts +51 -0
- package/scripts/lane/lane-spent.ts +6 -3
- package/scripts/lane/lane-stage-stats.ts +7 -2
- package/scripts/lane/lane-state.ts +302 -0
- package/scripts/lane/lane-stats.ts +58 -4
- package/scripts/lane/lane-stop-stats.ts +113 -37
- package/scripts/lane/lane-test-related-stats.ts +127 -0
- package/scripts/lane/lane-transcript-calls.ts +145 -0
- package/scripts/lane/lane-transcript-stats.ts +168 -0
- package/scripts/lane/lane-vacant.ts +13 -3
- package/scripts/lane/lane-withdraw-cli.ts +5 -4
- package/scripts/lane/lane-withdraw.ts +50 -49
- package/scripts/lane/process-directories.ts +70 -0
- package/scripts/lane/process-lane-marks.ts +94 -0
- package/scripts/lib/atomic-write.ts +62 -0
- package/scripts/lib/buffered-process.ts +12 -5
- package/scripts/lib/cli-flags.ts +53 -0
- package/scripts/lib/figure-format.ts +63 -0
- package/scripts/lib/json-value.ts +9 -1
- package/scripts/lib/package-manifest.ts +35 -0
- package/scripts/lib/read-file.ts +3 -1
- package/scripts/lib/stamped-memo.ts +63 -0
- package/scripts/lib/stdout-on-stderr.ts +24 -0
- package/scripts/lib/timeouts.ts +9 -0
- package/scripts/lib/transcript-record.ts +91 -0
- package/scripts/lines/effective-limit.ts +1 -1
- package/scripts/lint/eslint-cache-dependencies.ts +145 -0
- package/scripts/lint/eslint-daemon.ts +265 -0
- package/scripts/lint/format.ts +11 -2
- package/scripts/lint/import-graph.ts +279 -0
- package/scripts/lint/lint-gate-scope.ts +102 -0
- package/scripts/lint/lint-parallel-main.ts +19 -0
- package/scripts/lint/lint-parallel.ts +62 -30
- package/scripts/lint/lint-related-scope.ts +30 -24
- package/scripts/lint/lint-related.ts +38 -6
- package/scripts/notify/confirmation-options.ts +1 -2
- package/scripts/notify/telegram-notify.ts +20 -20
- package/scripts/notify/telegram-test-logic.ts +0 -1
- package/scripts/notify/telegram-test-main.ts +8 -0
- package/scripts/notify/telegram-test.ts +2 -6
- package/scripts/observations/observation-ledger.ts +13 -0
- package/scripts/observations/observations-flush-landing.ts +5 -3
- package/scripts/observations/observations-flush.ts +6 -1
- package/scripts/overrides/overrides-check.ts +2 -5
- package/scripts/overrides/overrides-files.ts +2 -1
- package/scripts/overrides/overrides-logic.ts +3 -6
- package/scripts/package/verify-optional-eslint-install.ts +8 -6
- package/scripts/propagate/propagate-publish.ts +2 -7
- package/scripts/propagate/propagate-targets.ts +4 -12
- package/scripts/refactor/import-graph.ts +9 -7
- package/scripts/release/github-release-runs.ts +17 -6
- package/scripts/release/release-approval-prune.ts +18 -0
- package/scripts/release/release-history.ts +1 -3
- package/scripts/release/release-progress.ts +42 -22
- package/scripts/release/release-publish-run.ts +39 -0
- package/scripts/release/release-publish.ts +6 -6
- package/scripts/release/release-scope-cli.ts +0 -10
- package/scripts/release/release-scope-main.ts +12 -0
- package/scripts/repo/repo-setting.ts +3 -6
- package/scripts/repo/required-checks-logic.ts +6 -15
- package/scripts/report/report-format-reference.ts +2 -0
- package/scripts/report/report-labels.ts +155 -0
- package/scripts/report/report-lint-cli.ts +3 -0
- package/scripts/report/report-lint.ts +85 -110
- package/scripts/report/report-template-cli.ts +59 -0
- package/scripts/report/report-template.ts +112 -0
- package/scripts/review/acceptance-map.ts +1 -0
- package/scripts/review/removed-tests-check.ts +86 -0
- package/scripts/review/removed-tests.ts +149 -0
- package/scripts/review/review-brief-cli.ts +81 -15
- package/scripts/review/review-brief-main.ts +12 -0
- package/scripts/review/review-brief.ts +31 -3
- package/scripts/review/review-code-tree.ts +10 -6
- package/scripts/review/review-comment-round.ts +26 -8
- package/scripts/review/review-finding-ledger.ts +30 -15
- package/scripts/review/review-interaction.ts +94 -0
- package/scripts/review/review-readers-scan.ts +163 -0
- package/scripts/review/review-record-cli.ts +69 -30
- package/scripts/review/review-record-main.ts +12 -0
- package/scripts/review/review-round-one-blobs.ts +221 -0
- package/scripts/review/review-round2-cli.ts +49 -12
- package/scripts/review/review-round2.ts +80 -12
- package/scripts/review/review-stamps.ts +19 -1
- package/scripts/review/review-stop-log.ts +96 -0
- package/scripts/review/review-stops-cli.ts +85 -0
- package/scripts/review/review-tree.ts +6 -17
- package/scripts/review/review-untouched-readers.ts +146 -0
- package/scripts/review/test-titles.ts +118 -0
- package/scripts/rules/agent-model.ts +8 -0
- package/scripts/rules/bash-triggers.ts +46 -3
- package/scripts/rules/decision-oracle-batch.ts +9 -1
- package/scripts/rules/decision-oracle.ts +2 -2
- package/scripts/rules/delivered-rules.ts +80 -2
- package/scripts/rules/destructive-command.ts +8 -1
- package/scripts/rules/direct-filing.ts +10 -2
- package/scripts/rules/direct-issue-comment.ts +117 -0
- package/scripts/rules/direct-pr-create.ts +69 -3
- package/scripts/rules/early-heartbeat.ts +2 -1
- package/scripts/rules/file-body.ts +7 -0
- package/scripts/rules/filing-cap.ts +7 -0
- package/scripts/rules/git-argv.ts +72 -14
- package/scripts/rules/git-force.ts +12 -2
- package/scripts/rules/guard-remedy.ts +4 -0
- package/scripts/rules/implementation-cut-verdict.ts +1 -1
- package/scripts/rules/implementation-cut.ts +11 -3
- package/scripts/rules/index-guard.ts +8 -1
- package/scripts/rules/issue-comments.ts +9 -1
- package/scripts/rules/josh-git-bare.ts +7 -0
- package/scripts/rules/lane-background.ts +67 -25
- package/scripts/rules/lane-carry-conflict.ts +6 -0
- package/scripts/rules/lane-direct-tool.ts +74 -0
- package/scripts/rules/lane-interactive-ask.ts +9 -2
- package/scripts/rules/lane-park.ts +45 -23
- package/scripts/rules/lane-split-park.ts +10 -2
- package/scripts/rules/lane-switch-main.ts +7 -0
- package/scripts/rules/oracle-consulted.ts +10 -0
- package/scripts/rules/oracle-firing.ts +1 -0
- package/scripts/rules/permission-guards.ts +3 -0
- package/scripts/rules/piped-verification.ts +1 -1
- package/scripts/rules/poll-loop.ts +7 -0
- package/scripts/rules/pr-merge.ts +123 -0
- package/scripts/rules/protected-files.ts +32 -5
- package/scripts/rules/raw-field-body.ts +6 -0
- package/scripts/rules/rule-body-guard.ts +8 -0
- package/scripts/rules/rule-list.ts +9 -28
- package/scripts/rules/rule-value-cache.ts +4 -9
- package/scripts/rules/rule-value-cli.ts +51 -32
- package/scripts/rules/rule-value.ts +1 -1
- package/scripts/rules/shell-segments.ts +35 -0
- package/scripts/rules/stop-rule-rows.ts +64 -0
- package/scripts/rules/stop-rules.ts +3 -4
- package/scripts/rules/test-declared-commit.ts +11 -1
- package/scripts/rules/third-party-write.ts +7 -0
- package/scripts/rules/timed-rerun.ts +69 -0
- package/scripts/rules/worktree-guard.ts +13 -3
- package/scripts/run/board/run-board-app-name.ts +23 -0
- package/scripts/run/board/run-board-cli.ts +65 -32
- package/scripts/run/board/run-board-earlier.ts +94 -0
- package/scripts/run/board/run-board-ending-reason.ts +67 -0
- package/scripts/run/board/run-board-estimate-schedule.ts +103 -0
- package/scripts/run/board/run-board-estimate.ts +98 -0
- package/scripts/run/board/run-board-fit.ts +153 -36
- package/scripts/run/board/run-board-fold.ts +123 -0
- package/scripts/run/board/run-board-fresh.ts +2 -2
- package/scripts/run/board/run-board-gather-live.ts +29 -0
- package/scripts/run/board/run-board-gather-thread.ts +189 -0
- package/scripts/run/board/run-board-gather-worker.ts +29 -0
- package/scripts/run/board/run-board-gather.ts +187 -0
- package/scripts/run/board/run-board-github.ts +118 -38
- package/scripts/run/board/run-board-header.ts +82 -20
- package/scripts/run/board/run-board-keys.ts +58 -0
- package/scripts/run/board/run-board-kind.ts +20 -6
- package/scripts/run/board/run-board-labels.ts +116 -71
- package/scripts/run/board/run-board-lane-state.ts +109 -0
- package/scripts/run/board/run-board-lane-time.ts +122 -0
- package/scripts/run/board/run-board-layout.ts +239 -60
- package/scripts/run/board/run-board-length.ts +52 -0
- package/scripts/run/board/run-board-machine.ts +8 -4
- package/scripts/run/board/run-board-main.ts +12 -0
- package/scripts/run/board/run-board-notes.ts +52 -31
- package/scripts/run/board/run-board-now.ts +1 -1
- package/scripts/run/board/run-board-operation.ts +15 -33
- package/scripts/run/board/run-board-phase.ts +1 -1
- package/scripts/run/board/run-board-plan-shared.ts +115 -0
- package/scripts/run/board/run-board-plan.ts +55 -3
- package/scripts/run/board/run-board-planning.ts +45 -0
- package/scripts/run/board/run-board-priority.ts +44 -0
- package/scripts/run/board/run-board-process-groups.ts +195 -0
- package/scripts/run/board/run-board-read.ts +151 -27
- package/scripts/run/board/run-board-reason.ts +235 -51
- package/scripts/run/board/run-board-released.ts +35 -0
- package/scripts/run/board/run-board-render-checks.ts +39 -5
- package/scripts/run/board/run-board-render-condition.ts +96 -0
- package/scripts/run/board/run-board-render-drawn.ts +67 -0
- package/scripts/run/board/run-board-render-foot.ts +59 -0
- package/scripts/run/board/run-board-render-icon.ts +35 -0
- package/scripts/run/board/run-board-render-lanes.ts +145 -0
- package/scripts/run/board/run-board-render-lead.ts +80 -0
- package/scripts/run/board/run-board-render-legend.ts +117 -48
- package/scripts/run/board/run-board-render-notes.ts +156 -29
- package/scripts/run/board/run-board-render-processes.ts +196 -0
- package/scripts/run/board/run-board-render-section.ts +119 -0
- package/scripts/run/board/run-board-render-slot.ts +3 -36
- package/scripts/run/board/run-board-render-tail.ts +115 -48
- package/scripts/run/board/run-board-render-time.ts +56 -0
- package/scripts/run/board/run-board-render-title.ts +117 -24
- package/scripts/run/board/run-board-render-waits.ts +104 -20
- package/scripts/run/board/run-board-render.ts +207 -265
- package/scripts/run/board/run-board-roots.ts +54 -0
- package/scripts/run/board/run-board-row-yield.ts +9 -4
- package/scripts/run/board/run-board-setup.ts +1 -12
- package/scripts/run/board/run-board-snapshot.ts +111 -0
- package/scripts/run/board/run-board-spin.ts +2 -2
- package/scripts/run/board/run-board-state.ts +22 -33
- package/scripts/run/board/run-board-status.ts +147 -71
- package/scripts/run/board/run-board-thanks-read.ts +2 -2
- package/scripts/run/board/run-board-tick.ts +122 -107
- package/scripts/run/board/run-board-usage-board.ts +78 -0
- package/scripts/run/board/run-board-usage-lanes.ts +113 -0
- package/scripts/run/board/run-board-usage-listing.ts +93 -0
- package/scripts/run/board/run-board-usage-read.ts +120 -150
- package/scripts/run/board/run-board-usage-rusage.ts +19 -7
- package/scripts/run/board/run-board-usage-text.ts +139 -51
- package/scripts/run/board/run-board-usage-top.ts +102 -0
- package/scripts/run/board/run-board-usage.ts +89 -72
- package/scripts/run/board/run-board-usual.ts +20 -9
- package/scripts/run/carry/run-carry-args.ts +4 -7
- package/scripts/run/carry/run-carry-conversation.ts +1 -1
- package/scripts/run/carry/run-carry.ts +9 -4
- package/scripts/run/cut/run-cut-handoff.ts +2 -2
- package/scripts/run/event/run-event-cli.ts +1 -5
- package/scripts/run/event/run-event-filed.ts +12 -6
- package/scripts/run/event/run-event-follow.ts +2 -3
- package/scripts/run/event/run-event-render.ts +6 -10
- package/scripts/run/event/run-event-scope.ts +2 -2
- package/scripts/run/event/run-event-stream-emit.ts +82 -22
- package/scripts/run/event/run-event-stream.ts +34 -8
- package/scripts/run/hold/run-hold-adopt.ts +65 -0
- package/scripts/run/hold/run-hold-cli.ts +93 -22
- package/scripts/run/hold/run-hold-main.ts +12 -0
- package/scripts/run/hold/run-hold-member.ts +37 -0
- package/scripts/run/hold/run-stop-notify.ts +183 -0
- package/scripts/run/merge/run-merge-auth.ts +31 -0
- package/scripts/run/merge/run-merge-cli.ts +62 -48
- package/scripts/run/merge/run-merge-close.ts +74 -0
- package/scripts/run/merge/run-merge-collect.ts +6 -13
- package/scripts/run/merge/run-merge-driver.ts +71 -0
- package/scripts/run/merge/run-merge-main.ts +12 -0
- package/scripts/run/merge/run-merge-outcome.ts +23 -0
- package/scripts/run/merge/run-merge-restart.ts +39 -0
- package/scripts/run/merge/run-merge-ship.ts +20 -4
- package/scripts/run/merge/run-merge-signals.ts +33 -9
- package/scripts/run/merge/run-merge-steps.ts +120 -85
- package/scripts/run/merge/run-merge.ts +38 -37
- package/scripts/run/park/run-park-cli.ts +91 -0
- package/scripts/run/park/run-park.ts +88 -0
- package/scripts/run/park/run-stalled.ts +68 -0
- package/scripts/run/pause/run-pause-cli.ts +73 -0
- package/scripts/run/pause/run-pause.ts +251 -0
- package/scripts/run/progress/run-progress-cli.ts +4 -12
- package/scripts/run/progress/run-progress-clock.ts +1 -1
- package/scripts/run/progress/run-progress-config.ts +6 -14
- package/scripts/run/progress/run-progress-main.ts +12 -0
- package/scripts/run/progress/run-progress.ts +1 -4
- package/scripts/run/review/run-review.ts +2 -1
- package/scripts/run/run-ending.ts +1 -0
- package/scripts/run/run-invocation.ts +5 -6
- package/scripts/run/run-liveness-cli.ts +6 -8
- package/scripts/run/run-liveness.ts +60 -29
- package/scripts/run/run-next.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +2 -2
- package/scripts/run/run-preflight.ts +18 -3
- package/scripts/run/run-prep-cli.ts +1 -10
- package/scripts/run/run-prep-locate.ts +7 -50
- package/scripts/run/run-prep-main.ts +12 -0
- package/scripts/run/run-prep.ts +6 -0
- package/scripts/run/run-report-cli.ts +101 -13
- package/scripts/run/run-report-handoff.ts +114 -0
- package/scripts/run/run-step.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +13 -0
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +1 -1
- package/scripts/run/ship/run-ship-args.ts +121 -0
- package/scripts/run/ship/run-ship-body-draft.ts +110 -0
- package/scripts/run/ship/run-ship-cli.ts +63 -140
- package/scripts/run/ship/run-ship-detach.ts +19 -8
- package/scripts/run/ship/run-ship-infra-retry.ts +59 -0
- package/scripts/run/ship/run-ship-next.ts +18 -2
- package/scripts/run/ship/run-ship-overlap.ts +42 -5
- package/scripts/run/ship/run-ship-place.ts +96 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +43 -0
- package/scripts/run/ship/run-ship-pre-detach.ts +81 -24
- package/scripts/run/ship/run-ship-preflight.ts +137 -32
- package/scripts/run/ship/run-ship-probe.ts +11 -3
- package/scripts/run/ship/run-ship-relaunch-cap.ts +70 -0
- package/scripts/run/ship/run-ship-removed-rows.ts +61 -0
- package/scripts/run/ship/run-ship-return.ts +107 -23
- package/scripts/run/ship/run-ship-review-steps.ts +105 -151
- package/scripts/run/ship/run-ship-review.ts +73 -6
- package/scripts/run/ship/run-ship-reviewer.ts +207 -0
- package/scripts/run/ship/run-ship-stage.ts +55 -6
- package/scripts/run/ship/run-ship-steps.ts +46 -15
- package/scripts/run/ship/run-ship-stop-cause.ts +135 -0
- package/scripts/run/ship/run-ship-stop-text.ts +11 -6
- package/scripts/run/ship/run-ship-sync.ts +43 -6
- package/scripts/run/ship/run-ship.ts +30 -2
- package/scripts/run/tidy/run-tidy-cli.ts +0 -4
- package/scripts/run/tidy/run-tidy-lanes.ts +3 -0
- package/scripts/run/tidy/run-tidy-main.ts +8 -0
- package/scripts/run/wake/run-wake-cli.ts +119 -100
- package/scripts/run/wake/run-wake-code.ts +83 -0
- package/scripts/run/wake/run-wake-driver.ts +19 -8
- package/scripts/run/wake/run-wake-loop.ts +34 -3
- package/scripts/run/wake/run-wake-main.ts +13 -0
- package/scripts/run/wake/run-wake-ports.ts +90 -0
- package/scripts/run/wake/run-wake-takeover.ts +66 -0
- package/scripts/run/wake/run-wake.ts +2 -1
- package/scripts/safe-chain/pack-hook.ts +2 -2
- package/scripts/safe-chain/preinstall-command.ts +2 -1
- package/scripts/safe-chain/project-config.ts +3 -9
- package/scripts/security/security-audit-main.ts +8 -0
- package/scripts/security/security-audit-provision-logic.ts +0 -11
- package/scripts/security/security-audit-provision-main.ts +12 -0
- package/scripts/security/security-audit-provision.ts +11 -26
- package/scripts/security/security-audit.ts +6 -7
- package/scripts/self-sync-guard/self-sync-guard-logic.ts +6 -13
- package/scripts/split/split-assess-cli.ts +2 -2
- package/scripts/split/split-assess.ts +15 -0
- package/scripts/split/split-measure.ts +65 -0
- package/scripts/sync/claude-settings-merge.ts +8 -15
- package/scripts/sync/directory-copy-guard.ts +4 -9
- package/scripts/sync/file-content.ts +78 -8
- package/scripts/sync/sync-ai-files.ts +3 -5
- package/scripts/sync/sync-configs.ts +11 -23
- package/scripts/sync/sync-hook-safety.ts +4 -10
- package/scripts/sync/sync-main.ts +11 -0
- package/scripts/sync/sync.ts +25 -37
- package/scripts/test/test-declared-changed.ts +2 -2
- package/scripts/test/test-declared.ts +1 -1
- package/scripts/test/test-e2e-guard.ts +25 -1
- package/scripts/test/test-e2e-stamp.ts +38 -0
- package/scripts/test/test-pinned-logic.ts +145 -0
- package/scripts/test/test-pinned-main.ts +78 -0
- package/scripts/test/test-pinned.ts +100 -0
- package/scripts/test/test-read-map.ts +264 -0
- package/scripts/test/test-read-recording.ts +91 -0
- package/scripts/test/test-read-trace.ts +148 -0
- package/scripts/test/test-red-main.ts +12 -0
- package/scripts/test/test-red.ts +12 -30
- package/scripts/test/test-related-main.ts +18 -0
- package/scripts/test/test-related-record.ts +119 -0
- package/scripts/test/test-related-scope.ts +229 -43
- package/scripts/test/test-related.ts +300 -22
- package/scripts/test/test-unit-guard-main.ts +15 -0
- package/scripts/test/test-unit-guard.ts +42 -24
- package/scripts/test/unit-count-file.ts +37 -0
- package/scripts/test/unit-run-marker.ts +162 -0
- package/scripts/test/unit-setup-files.ts +51 -0
- package/scripts/test/unit-worker-share.ts +61 -149
- package/scripts/test/whole-suite-files.ts +63 -0
- package/scripts/time-runtime/time-background.ts +20 -2
- package/scripts/time-runtime/time-bundles.ts +4 -3
- package/scripts/time-runtime/time-density-hook.ts +8 -6
- package/scripts/time-runtime/time-followup-stage.ts +1 -3
- package/scripts/time-runtime/time-format.ts +5 -28
- package/scripts/time-runtime/time-markers.ts +10 -1
- package/scripts/time-runtime/time-shell.ts +97 -10
- package/scripts/time-runtime/time-transcript-line.ts +49 -62
- package/scripts/uninstall/uninstall.ts +1 -1
- package/scripts/version/bump-version-main.ts +12 -0
- package/scripts/version/bump-version.ts +25 -25
- package/scripts/version/effective-upstream.ts +6 -12
- package/scripts/version/latest-corepack.ts +17 -32
- package/scripts/version/latest-if-required-cli.ts +93 -0
- package/scripts/version/latest-lane-guard-main.ts +8 -0
- package/scripts/version/latest-lane-guard.ts +0 -4
- package/scripts/version/latest-scope-cli.ts +0 -9
- package/scripts/version/latest-scope-main.ts +12 -0
- package/scripts/version/latest-update.ts +5 -5
- package/scripts/version/npm-registry.ts +44 -6
- package/scripts/version/publishable-range-check.ts +5 -16
- package/scripts/version/release-age.ts +1 -1
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/release-window.ts +38 -0
- package/scripts/version/running-binary.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +3 -8
- package/scripts/version/version-targets.ts +6 -28
- package/templates/dependabot.yml +29 -0
- package/templates/gitignore +4 -1
- package/templates/pnpm-workspace.basic.yaml +1 -1
- package/templates/sonar-project.properties +3 -2
- package/templates/workflows/ci.yml +12 -4
- package/templates/workflows/github-release.yml +4 -0
- package/tsconfig.sonar.json +1 -1
- package/.ncurc.json +0 -3
- package/dist/hooks/batch-guard.js +0 -29
- package/dist/hooks/chunk-4KKD2UTK.js +0 -66
- package/dist/hooks/chunk-6TS2JD6D.js +0 -237
- package/dist/hooks/chunk-7DSBJVFH.js +0 -50
- package/dist/hooks/chunk-A65Y4YOR.js +0 -79
- package/dist/hooks/chunk-CDY4NFQG.js +0 -15
- package/dist/hooks/chunk-EFXE6C37.js +0 -81
- package/dist/hooks/chunk-GP4IEC4O.js +0 -20
- package/dist/hooks/chunk-JP45SOCJ.js +0 -56
- package/dist/hooks/chunk-LAOV7NLM.js +0 -45
- package/dist/hooks/chunk-M4HGZV7U.js +0 -83
- package/dist/hooks/chunk-PF2NZL2A.js +0 -131
- package/dist/hooks/chunk-PTICX4OL.js +0 -358
- package/dist/hooks/chunk-RT27QRJW.js +0 -6247
- package/dist/hooks/chunk-SYOTDYCI.js +0 -3533
- package/dist/hooks/chunk-UPUZF3AN.js +0 -279
- package/dist/hooks/chunk-WK6SMBIC.js +0 -3180
- package/dist/hooks/duplicate-read-guard.js +0 -30
- package/dist/hooks/investigation-guard.js +0 -29
- package/scripts/backlog/backlog-stalled-cli.ts +0 -30
- package/scripts/cost-runtime/cost-format.ts +0 -29
- package/scripts/init/basic-path-migration.ts +0 -24
- package/scripts/issue/issue-backlinks-cli.ts +0 -93
- package/scripts/josh/josh-commands-guard.ts +0 -38
- package/scripts/josh/josh-usage-automation.ts +0 -121
- package/scripts/josh/josh-usage-backlog.ts +0 -180
- package/scripts/josh/josh-usage-developer.ts +0 -230
- package/scripts/josh/josh-usage-hooks.ts +0 -38
- package/scripts/josh/josh-usage-issue.ts +0 -205
- package/scripts/josh/josh-usage-review.ts +0 -101
- package/scripts/josh/josh-usage-run.ts +0 -252
- package/scripts/josh/josh-usage-session.ts +0 -238
- package/scripts/josh/josh-usage-workflow.ts +0 -212
- package/scripts/lint/cross-file-recheck.ts +0 -155
- package/scripts/registry-migration/migrate-logic.ts +0 -237
- package/scripts/registry-migration/migrate.ts +0 -292
- package/scripts/registry-migration/user-npmrc.ts +0 -15
- package/scripts/rules/rule-guard.ts +0 -23
- package/scripts/rules/rule-registry-rows.ts +0 -292
- package/scripts/rules/rule-registry.ts +0 -112
- package/scripts/run/run-stranded-cli.ts +0 -27
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { test_declared_logic } from './test-declared-logic'
|
|
2
|
+
import { related_scope } from './test-related-scope'
|
|
3
|
+
|
|
4
|
+
// The pure half of `josh test:pinned` (joshuafolkken/kit#4516): read a `git diff -U0` into hunks, put
|
|
5
|
+
// one hunk's old lines back into the changed file, and split the hunks at the run's cap. The run itself
|
|
6
|
+
// is `test-pinned.ts`.
|
|
7
|
+
|
|
8
|
+
const DEFAULT_MAX_HUNKS = 20
|
|
9
|
+
const HEADER = /^@@ -\d+(?:,\d+)? \+(?<start>\d+)(?:,(?<count>\d+))? @@/u
|
|
10
|
+
const FILE_HEADER = '+++ b/'
|
|
11
|
+
const SECTION_HEADER = 'diff --git '
|
|
12
|
+
const OLD_LINE = '-'
|
|
13
|
+
const OLD_FILE_HEADER = '--- '
|
|
14
|
+
const LINE_BREAK = '\n'
|
|
15
|
+
const GREEN_REASON = 'reverting it left every related test green'
|
|
16
|
+
const NO_TEST_REASON = 'no related test ran with it reverted'
|
|
17
|
+
|
|
18
|
+
interface Hunk {
|
|
19
|
+
file: string
|
|
20
|
+
// The first changed line of the working file; for a pure deletion, the line the removed ones sat after.
|
|
21
|
+
new_start: number
|
|
22
|
+
new_count: number
|
|
23
|
+
old_lines: Array<string>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface HunkSplit {
|
|
27
|
+
checked: ReadonlyArray<Hunk>
|
|
28
|
+
unchecked: ReadonlyArray<Hunk>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// A reverted hunk nothing went red on: `is_green` when its related tests ran and passed, otherwise none
|
|
32
|
+
// ran at all — no related test, or a vitest that stopped before running one.
|
|
33
|
+
interface UnpinnedHunk extends Hunk {
|
|
34
|
+
is_green: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PinnedReport {
|
|
38
|
+
unpinned: ReadonlyArray<UnpinnedHunk>
|
|
39
|
+
unchecked: ReadonlyArray<Hunk>
|
|
40
|
+
max_hunks: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface DiffWalk {
|
|
44
|
+
file: string
|
|
45
|
+
hunks: Array<Hunk>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// A test file is never reverted, and only a file vitest can walk an import graph back from has related
|
|
49
|
+
// tests to run.
|
|
50
|
+
function is_target(file: string): boolean {
|
|
51
|
+
return !test_declared_logic.is_test_file(file) && related_scope.is_relatable(file)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// An omitted count is one line (`@@ -3 +3 @@`).
|
|
55
|
+
function count_of(group: string | undefined): number {
|
|
56
|
+
return group === undefined ? 1 : Number(group)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function hunk_from(file: string, line: string): Hunk | undefined {
|
|
60
|
+
const groups = HEADER.exec(line)?.groups
|
|
61
|
+
if (groups === undefined) return undefined
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
file,
|
|
65
|
+
new_start: Number(groups['start']),
|
|
66
|
+
new_count: count_of(groups['count']),
|
|
67
|
+
old_lines: [],
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function is_old_line(line: string): boolean {
|
|
72
|
+
return line.startsWith(OLD_LINE) && !line.startsWith(OLD_FILE_HEADER)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Each file section starts unnamed, so a `+++` line this does not read (a quoted path with a space)
|
|
76
|
+
// leaves its hunks on no file rather than on the previous one.
|
|
77
|
+
function file_after(file: string, line: string): string {
|
|
78
|
+
if (line.startsWith(FILE_HEADER)) return line.slice(FILE_HEADER.length)
|
|
79
|
+
|
|
80
|
+
return line.startsWith(SECTION_HEADER) ? '' : file
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function step(walk: DiffWalk, line: string): void {
|
|
84
|
+
walk.file = file_after(walk.file, line)
|
|
85
|
+
const hunk = hunk_from(walk.file, line)
|
|
86
|
+
|
|
87
|
+
if (hunk !== undefined) walk.hunks.push(hunk)
|
|
88
|
+
else if (is_old_line(line)) walk.hunks.at(-1)?.old_lines.push(line.slice(OLD_LINE.length))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// One `-U0` diff into its hunks, in diff order. The `+` lines are not kept: the working file already
|
|
92
|
+
// holds them, at `new_start` for `new_count` lines.
|
|
93
|
+
function parse_hunks(diff: string): Array<Hunk> {
|
|
94
|
+
const walk: DiffWalk = { file: '', hunks: [] }
|
|
95
|
+
|
|
96
|
+
for (const line of diff.split(LINE_BREAK)) step(walk, line)
|
|
97
|
+
|
|
98
|
+
return walk.hunks.filter((hunk) => is_target(hunk.file))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// A pure deletion's `new_start` names the line before the gap, so the old lines go in after it.
|
|
102
|
+
function start_index(hunk: Hunk): number {
|
|
103
|
+
return hunk.new_count === 0 ? hunk.new_start : hunk.new_start - 1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The working file's content with this one hunk put back to the base's lines.
|
|
107
|
+
function revert(content: string, hunk: Hunk): string {
|
|
108
|
+
const lines = content.split(LINE_BREAK)
|
|
109
|
+
|
|
110
|
+
lines.splice(start_index(hunk), hunk.new_count, ...hunk.old_lines)
|
|
111
|
+
|
|
112
|
+
return lines.join(LINE_BREAK)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function split(hunks: ReadonlyArray<Hunk>, max_hunks: number): HunkSplit {
|
|
116
|
+
return { checked: hunks.slice(0, max_hunks), unchecked: hunks.slice(max_hunks) }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function location(hunk: Hunk): string {
|
|
120
|
+
return `${hunk.file}:${String(Math.max(hunk.new_start, 1))}`
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function format(report: PinnedReport): string {
|
|
124
|
+
const unpinned = report.unpinned.map(
|
|
125
|
+
(hunk) => `unpinned ${location(hunk)} — ${hunk.is_green ? GREEN_REASON : NO_TEST_REASON}`,
|
|
126
|
+
)
|
|
127
|
+
const unchecked = report.unchecked.map(
|
|
128
|
+
(hunk) => `unchecked ${location(hunk)} — over the ${String(report.max_hunks)}-hunk cap`,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return [...unpinned, ...unchecked].join(LINE_BREAK)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const test_pinned_logic = {
|
|
135
|
+
DEFAULT_MAX_HUNKS,
|
|
136
|
+
format,
|
|
137
|
+
is_target,
|
|
138
|
+
location,
|
|
139
|
+
parse_hunks,
|
|
140
|
+
revert,
|
|
141
|
+
split,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type { Hunk, PinnedReport, UnpinnedHunk }
|
|
145
|
+
export { test_pinned_logic }
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { parseArgs } from 'node:util'
|
|
4
|
+
import { test_pinned } from './test-pinned'
|
|
5
|
+
import { test_pinned_logic, type PinnedReport } from './test-pinned-logic'
|
|
6
|
+
|
|
7
|
+
// The `josh test:pinned` entry: read `--max`, run, print one line per unpinned or unchecked hunk, and
|
|
8
|
+
// exit non-zero when any checked hunk is unpinned. An unchecked hunk alone does not fail the run — it
|
|
9
|
+
// was not asked about.
|
|
10
|
+
|
|
11
|
+
const CLEAN_EXIT = 0
|
|
12
|
+
const FAILED_EXIT = 1
|
|
13
|
+
const ARGV_OFFSET = 2
|
|
14
|
+
const USAGE = [
|
|
15
|
+
'Usage: josh test:pinned [--max <n>] [--help]',
|
|
16
|
+
' revert each changed hunk of a non-test code file and run its related tests;',
|
|
17
|
+
' prints `unpinned <file>:<line>` for a hunk no test went red on, and',
|
|
18
|
+
` \`unchecked <file>:<line>\` past the --max cap (default ${String(test_pinned_logic.DEFAULT_MAX_HUNKS)})`,
|
|
19
|
+
].join('\n')
|
|
20
|
+
|
|
21
|
+
const PARSE_ARGS_OPTIONS = {
|
|
22
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
23
|
+
max: { type: 'string' },
|
|
24
|
+
} as const
|
|
25
|
+
|
|
26
|
+
const HELP_FLAGS: ReadonlySet<string> = new Set(['--help', '-h'])
|
|
27
|
+
|
|
28
|
+
// A `--max` that is not a whole number above zero is a refusal, not the default.
|
|
29
|
+
function valid_max(raw: string | undefined): number | undefined {
|
|
30
|
+
const max = Number(raw ?? test_pinned_logic.DEFAULT_MAX_HUNKS)
|
|
31
|
+
|
|
32
|
+
return Number.isSafeInteger(max) && max > 0 ? max : undefined
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// `undefined` prints the usage: a help request, an unknown flag, or a missing or invalid `--max`.
|
|
36
|
+
function max_hunks_of(argv: ReadonlyArray<string>): number | undefined {
|
|
37
|
+
try {
|
|
38
|
+
const { values } = parseArgs({ args: [...argv], options: PARSE_ARGS_OPTIONS, strict: true })
|
|
39
|
+
|
|
40
|
+
return values.help ? undefined : valid_max(values.max)
|
|
41
|
+
} catch {
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Clean for an explicit help request; a refusal for anything the parse did not understand.
|
|
47
|
+
function print_usage(argv: ReadonlyArray<string>): number {
|
|
48
|
+
process.stderr.write(`${USAGE}\n`)
|
|
49
|
+
|
|
50
|
+
return argv.some((argument) => HELP_FLAGS.has(argument)) ? CLEAN_EXIT : FAILED_EXIT
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function output(report: PinnedReport): string {
|
|
54
|
+
const unpinned = String(report.unpinned.length)
|
|
55
|
+
const unchecked = String(report.unchecked.length)
|
|
56
|
+
const lines = [`unpinned ${unpinned} · unchecked ${unchecked}`, test_pinned_logic.format(report)]
|
|
57
|
+
|
|
58
|
+
return `${lines.filter((line) => line !== '').join('\n')}\n`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function main(argv: ReadonlyArray<string>): Promise<number> {
|
|
62
|
+
const max_hunks = max_hunks_of(argv)
|
|
63
|
+
if (max_hunks === undefined) return print_usage(argv)
|
|
64
|
+
|
|
65
|
+
const report = await test_pinned.run(max_hunks)
|
|
66
|
+
|
|
67
|
+
process.stdout.write(output(report))
|
|
68
|
+
|
|
69
|
+
return report.unpinned.length === 0 ? CLEAN_EXIT : FAILED_EXIT
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
73
|
+
process.exitCode = await main(process.argv.slice(ARGV_OFFSET))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const test_pinned_main = { main }
|
|
77
|
+
|
|
78
|
+
export { test_pinned_main }
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { base_tree, type BaseTree } from '#scripts/git/base-tree'
|
|
5
|
+
import { change_base } from '#scripts/git/change-base'
|
|
6
|
+
import { changed_paths } from '#scripts/git/changed-paths'
|
|
7
|
+
import { git_spawn } from '#scripts/git/git-spawn'
|
|
8
|
+
import {
|
|
9
|
+
test_pinned_logic,
|
|
10
|
+
type Hunk,
|
|
11
|
+
type PinnedReport,
|
|
12
|
+
type UnpinnedHunk,
|
|
13
|
+
} from './test-pinned-logic'
|
|
14
|
+
import { test_red } from './test-red'
|
|
15
|
+
import type { RedVerdict } from './test-red-logic'
|
|
16
|
+
|
|
17
|
+
// `josh test:pinned` (joshuafolkken/kit#4516) — put each changed hunk of a non-test code file back to
|
|
18
|
+
// the merge-base's lines, one at a time, run that file's related tests, and list the hunks no test went
|
|
19
|
+
// red on: the "reverting it stays green" finding a review used to stop a lane on.
|
|
20
|
+
//
|
|
21
|
+
// **The reverts happen in a detached worktree at HEAD, never the caller's checkout** (`base-tree.ts`).
|
|
22
|
+
// The changed files are copied in from the working tree first, so what is reverted is the change as it
|
|
23
|
+
// stands, committed or not, and each hunk's file is restored before the next one runs.
|
|
24
|
+
|
|
25
|
+
const TREE_PREFIX = 'josh-test-pinned-'
|
|
26
|
+
const HEAD = 'HEAD'
|
|
27
|
+
|
|
28
|
+
// `--diff-filter=M`: an added file's one hunk is the whole file, and reverting it is a missing module
|
|
29
|
+
// every importer fails on, which says nothing about the tests. The prefixes are pinned: a user's
|
|
30
|
+
// `diff.mnemonicPrefix` writes `+++ w/<file>`, which would read as no hunk at all.
|
|
31
|
+
async function read_hunks(base: string, files: ReadonlyArray<string>): Promise<Array<Hunk>> {
|
|
32
|
+
if (files.length === 0) return []
|
|
33
|
+
const diff_args = ['diff', '-U0', '--no-color', '--no-ext-diff', '--diff-filter=M']
|
|
34
|
+
const prefix_args = ['--src-prefix=a/', '--dst-prefix=b/', base, '--']
|
|
35
|
+
|
|
36
|
+
return test_pinned_logic.parse_hunks(
|
|
37
|
+
await git_spawn.read([...diff_args, ...prefix_args, ...files]),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The verdict with this one hunk reverted. The file is put back before the next hunk runs, or that
|
|
42
|
+
// hunk's tests would also see this revert.
|
|
43
|
+
async function verdict_of(tree: BaseTree, hunk: Hunk): Promise<RedVerdict> {
|
|
44
|
+
const target = path.join(tree.tree, hunk.file)
|
|
45
|
+
const content = await readFile(target, 'utf8')
|
|
46
|
+
|
|
47
|
+
await writeFile(target, test_pinned_logic.revert(content, hunk))
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return await test_red.verdict_in(tree, ['related', hunk.file, '--run'])
|
|
51
|
+
} finally {
|
|
52
|
+
await writeFile(target, content)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// One hunk at a time: two reverts in flight would share the tree, and each run is already a whole vitest.
|
|
57
|
+
// Green and no-test both leave a hunk unpinned: either way nothing went red when it was reverted.
|
|
58
|
+
async function unpinned_in(
|
|
59
|
+
tree: BaseTree,
|
|
60
|
+
hunks: ReadonlyArray<Hunk>,
|
|
61
|
+
): Promise<Array<UnpinnedHunk>> {
|
|
62
|
+
const unpinned: Array<UnpinnedHunk> = []
|
|
63
|
+
|
|
64
|
+
for (const hunk of hunks) {
|
|
65
|
+
// eslint-disable-next-line no-await-in-loop -- the hunks share one tree, so each revert waits for the last restore
|
|
66
|
+
const verdict = await verdict_of(tree, hunk)
|
|
67
|
+
if (verdict !== 'red') unpinned.push({ ...hunk, is_green: verdict === 'green' })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return unpinned
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function unpinned_on(
|
|
74
|
+
paths: ReadonlyArray<string>,
|
|
75
|
+
hunks: ReadonlyArray<Hunk>,
|
|
76
|
+
): Promise<Array<UnpinnedHunk>> {
|
|
77
|
+
if (hunks.length === 0) return []
|
|
78
|
+
|
|
79
|
+
return await base_tree.with_tree(TREE_PREFIX, HEAD, async (tree) => {
|
|
80
|
+
await base_tree.copy_in(tree.tree, paths)
|
|
81
|
+
|
|
82
|
+
return await unpinned_in(tree, hunks)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// No merge-base (not a repository, no default branch) checks nothing, as `josh test:red` does.
|
|
87
|
+
async function run(max_hunks: number): Promise<PinnedReport> {
|
|
88
|
+
const base = await change_base.resolved()
|
|
89
|
+
const changed = await changed_paths.read_changed_paths(false)
|
|
90
|
+
const paths = changed.filter((file) => existsSync(file))
|
|
91
|
+
const targets = paths.filter((file) => test_pinned_logic.is_target(file))
|
|
92
|
+
const hunks = base === undefined ? [] : await read_hunks(base, targets)
|
|
93
|
+
const { checked, unchecked } = test_pinned_logic.split(hunks, max_hunks)
|
|
94
|
+
|
|
95
|
+
return { unpinned: await unpinned_on(paths, checked), unchecked, max_hunks }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const test_pinned = { run }
|
|
99
|
+
|
|
100
|
+
export { test_pinned }
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, rmSync, statSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { git_common_directory } from '#scripts/git/git-common-directory'
|
|
4
|
+
import { atomic_write } from '#scripts/lib/atomic-write'
|
|
5
|
+
import { json_value } from '#scripts/lib/json-value'
|
|
6
|
+
import { test_read_trace, type ReadRecords } from './test-read-trace'
|
|
7
|
+
|
|
8
|
+
// The test-read map: for every repository path a full unit run read, the modules that read it.
|
|
9
|
+
// `josh test:related` resolves a changed non-code file through it to the code files `vitest related`
|
|
10
|
+
// can walk, so a changed document runs the tests that read it rather than the whole suite.
|
|
11
|
+
//
|
|
12
|
+
// **The map is valid against the tree it was recorded on, and only that tree.** It records the
|
|
13
|
+
// commit and the files that differed from it, so the change a narrowing reads is the change since
|
|
14
|
+
// then. A tree whose own code changed what it reads has changed that code, and `vitest related`
|
|
15
|
+
// runs the tests importing it. **It lives in the git common directory**, which every lane worktree of
|
|
16
|
+
// one clone shares along with the commits it names, so one lane's full run narrows the next lane's.
|
|
17
|
+
// **One file per version** (joshuafolkken/kit#4423): a lane on an older kit writing its map leaves a
|
|
18
|
+
// newer lane's map in place, so a version bump no longer sends either to the whole suite.
|
|
19
|
+
|
|
20
|
+
interface ReadMap {
|
|
21
|
+
version: number
|
|
22
|
+
commit: string
|
|
23
|
+
// The files that differed from `commit` when the map was recorded, each with the digest of its
|
|
24
|
+
// content then (`input_fingerprint.of_files`), so one whose content has not moved since does not count as changed
|
|
25
|
+
// (joshuafolkken/kit#4425).
|
|
26
|
+
dirty: Record<string, string>
|
|
27
|
+
// The directories `commit` holds, so a directory new since then is told apart from one no test
|
|
28
|
+
// read — the map's own keys cannot say which, since an unread directory has none.
|
|
29
|
+
directories: Array<string>
|
|
30
|
+
readers: ReadRecords
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Bumped whenever the shape, what a reader means or how `dirty` is digested changes, so a map an older
|
|
34
|
+
// kit wrote reads as no map at all and the next full run writes it afresh. 6: `dirty` is
|
|
35
|
+
// `input-fingerprint.ts`'s digest of every changed file (joshuafolkken/kit#4497).
|
|
36
|
+
const MAP_VERSION = 6
|
|
37
|
+
|
|
38
|
+
const MAP_DIRECTORY = 'josh'
|
|
39
|
+
const MAP_FILE_STEM = 'test-read-map'
|
|
40
|
+
const RECORD_EXTENSION = '.json'
|
|
41
|
+
|
|
42
|
+
// A map file of another version no lane has written for this long belongs to no kit still running,
|
|
43
|
+
// and is removed by the next write: one week.
|
|
44
|
+
const STALE_AFTER_MS = 604_800_000
|
|
45
|
+
|
|
46
|
+
type ReadPair = [string, string]
|
|
47
|
+
|
|
48
|
+
function record_pairs(record: ReadRecords): Array<ReadPair> {
|
|
49
|
+
return Object.entries(record).flatMap(([read_path, readers]) =>
|
|
50
|
+
readers.map((reader): ReadPair => [read_path, reader]),
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function merge_records(records: ReadonlyArray<ReadRecords>): ReadRecords {
|
|
55
|
+
const merged = new Map<string, Set<string>>()
|
|
56
|
+
const pairs = records.flatMap((record) => record_pairs(record))
|
|
57
|
+
|
|
58
|
+
for (const [read_path, reader] of pairs) {
|
|
59
|
+
test_read_trace.add_reader(merged, read_path, reader)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return test_read_trace.to_records(merged)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function is_object(value: unknown): value is object {
|
|
66
|
+
return typeof value === 'object' && value !== null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function is_string_array(value: unknown): value is Array<string> {
|
|
70
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === 'string')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function is_read_records(value: unknown): value is ReadRecords {
|
|
74
|
+
return is_object(value) && Object.values(value).every(is_string_array)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function is_digest_record(value: unknown): value is Record<string, string> {
|
|
78
|
+
if (!is_object(value) || Array.isArray(value)) return false
|
|
79
|
+
|
|
80
|
+
return Object.values(value).every((digest) => typeof digest === 'string')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const FIELD_CHECKS: ReadonlyArray<[keyof ReadMap, (field: unknown) => boolean]> = [
|
|
84
|
+
['version', (field) => field === MAP_VERSION],
|
|
85
|
+
['commit', (field) => typeof field === 'string'],
|
|
86
|
+
['dirty', is_digest_record],
|
|
87
|
+
['directories', is_string_array],
|
|
88
|
+
['readers', is_read_records],
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
function is_read_map(value: unknown): value is ReadMap {
|
|
92
|
+
if (!is_object(value)) return false
|
|
93
|
+
|
|
94
|
+
return FIELD_CHECKS.every(([field, is_valid]) => is_valid(Reflect.get(value, field)))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// **The files whose content differs from the tree the map was recorded on** (joshuafolkken/kit#4425).
|
|
98
|
+
// `since` is every path that differs from `map.commit` now, and `current` the digest of every path
|
|
99
|
+
// that was dirty then. A path dirty then is changed only when its digest moved: counted by name, a
|
|
100
|
+
// lane's own change the map was recorded over read as changed on every later run, so a gate reused
|
|
101
|
+
// no green unit step whenever the lane held any code.
|
|
102
|
+
function changed_since_tree(
|
|
103
|
+
map: ReadMap,
|
|
104
|
+
since: ReadonlyArray<string>,
|
|
105
|
+
current: Readonly<Record<string, string>>,
|
|
106
|
+
): Array<string> {
|
|
107
|
+
const fresh = since.filter((file) => !Object.hasOwn(map.dirty, file))
|
|
108
|
+
const moved = Object.entries(map.dirty)
|
|
109
|
+
.filter(([file, digest]) => current[file] !== digest)
|
|
110
|
+
.map(([file]) => file)
|
|
111
|
+
|
|
112
|
+
return [...new Set([...fresh, ...moved])]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parse_map(text: string): ReadMap | undefined {
|
|
116
|
+
const value = json_value.parse_or_undefined(text)
|
|
117
|
+
|
|
118
|
+
return is_read_map(value) ? value : undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function parse_records(text: string): ReadRecords {
|
|
122
|
+
const value = json_value.parse_or_undefined(text)
|
|
123
|
+
|
|
124
|
+
return is_read_records(value) ? value : {}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Innermost first, ending at the root `.`.
|
|
128
|
+
function directories_above(relative: string): Array<string> {
|
|
129
|
+
const parent = path.dirname(relative)
|
|
130
|
+
|
|
131
|
+
if (parent === relative) return []
|
|
132
|
+
|
|
133
|
+
return [parent, ...directories_above(parent)]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The directories on the file's path the recorded commit did not hold, innermost first. The root
|
|
137
|
+
// always existed.
|
|
138
|
+
function created_directories(
|
|
139
|
+
directories: ReadonlyArray<string>,
|
|
140
|
+
recorded: ReadonlySet<string>,
|
|
141
|
+
): Array<string> {
|
|
142
|
+
return directories.filter(
|
|
143
|
+
(directory) => directory !== test_read_trace.ROOT_ENTRY && !recorded.has(directory),
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// A changed file is read by whoever read it, listed its own directory or walked any directory above
|
|
148
|
+
// it. A directory new since the recording is also a new entry in the listing above it and a probe of
|
|
149
|
+
// it that now answers differently; a directory that already existed reads the same to both, whatever
|
|
150
|
+
// changed deeper down.
|
|
151
|
+
function lookup_keys(relative: string, recorded: ReadonlySet<string>): Array<string> {
|
|
152
|
+
const directories = directories_above(relative)
|
|
153
|
+
const created = created_directories(directories, recorded)
|
|
154
|
+
const listed = [
|
|
155
|
+
directories[0] ?? test_read_trace.ROOT_ENTRY,
|
|
156
|
+
...created.map((directory) => path.dirname(directory)),
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
return [
|
|
160
|
+
relative,
|
|
161
|
+
...created,
|
|
162
|
+
...listed.map((directory) => test_read_trace.read_key(directory, 'listing')),
|
|
163
|
+
...directories.map((directory) => test_read_trace.read_key(directory, 'walk')),
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function readers_of(map: ReadMap, relative: string): Array<string> {
|
|
168
|
+
const keys = lookup_keys(relative, new Set(map.directories))
|
|
169
|
+
|
|
170
|
+
return [...new Set(keys.flatMap((key) => map.readers[key] ?? []))]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// **A directory the recorded tree held, inside which no recorded test listed, walked or read any
|
|
174
|
+
// entry** (joshuafolkken/kit#4303). A file added to it since is one no test reads, as surely as one
|
|
175
|
+
// the tree already held there: a test that builds a path into it reads some file there, and one that
|
|
176
|
+
// enumerates it lists it, both of which leave a key whose directory is this one.
|
|
177
|
+
function in_unread_directory(map: ReadMap, relative: string): boolean {
|
|
178
|
+
const directory = path.dirname(relative)
|
|
179
|
+
|
|
180
|
+
if (!map.directories.includes(directory)) return false
|
|
181
|
+
|
|
182
|
+
return Object.keys(map.readers).every((key) => path.dirname(key) !== directory)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function map_path(root: string, version: number = MAP_VERSION): string | undefined {
|
|
186
|
+
const common = git_common_directory.repository(root)
|
|
187
|
+
const name = `${MAP_FILE_STEM}.v${String(version)}${RECORD_EXTENSION}`
|
|
188
|
+
|
|
189
|
+
return common === undefined ? undefined : path.join(common, MAP_DIRECTORY, name)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Every version's map, the unversioned one kits before joshuafolkken/kit#4423 wrote included; a
|
|
193
|
+
// staging file ends in `.tmp`, not the extension.
|
|
194
|
+
function is_map_file(name: string): boolean {
|
|
195
|
+
return name.startsWith(MAP_FILE_STEM) && name.endsWith(RECORD_EXTENSION)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// A file another lane's write pruned meanwhile has no stat, and is not this write's to remove.
|
|
199
|
+
function is_stale(file_path: string, now: number): boolean {
|
|
200
|
+
const modified = statSync(file_path, { throwIfNoEntry: false })?.mtimeMs ?? now
|
|
201
|
+
|
|
202
|
+
return now - modified > STALE_AFTER_MS
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// The map just written is never stale, so it survives its own prune.
|
|
206
|
+
function prune_stale_maps(directory: string): void {
|
|
207
|
+
const now = Date.now()
|
|
208
|
+
const maps = readdirSync(directory)
|
|
209
|
+
.filter((name) => is_map_file(name))
|
|
210
|
+
.map((name) => path.join(directory, name))
|
|
211
|
+
|
|
212
|
+
for (const file_path of maps) {
|
|
213
|
+
if (is_stale(file_path, now)) rmSync(file_path, { force: true })
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function read_text(file_path: string): string | undefined {
|
|
218
|
+
try {
|
|
219
|
+
return readFileSync(file_path, 'utf8')
|
|
220
|
+
} catch {
|
|
221
|
+
return undefined
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function read_map(root: string): ReadMap | undefined {
|
|
226
|
+
const file_path = map_path(root)
|
|
227
|
+
const text = file_path === undefined ? undefined : read_text(file_path)
|
|
228
|
+
|
|
229
|
+
return text === undefined ? undefined : parse_map(text)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Written beside the target and renamed over it, so a lane reading the map while another lane
|
|
233
|
+
// replaces it reads one whole map or the other. It lands in its own version's file.
|
|
234
|
+
function write_map(root: string, map: ReadMap): void {
|
|
235
|
+
const file_path = map_path(root, map.version)
|
|
236
|
+
|
|
237
|
+
if (file_path === undefined) return
|
|
238
|
+
|
|
239
|
+
atomic_write.replace_file(file_path, JSON.stringify(map))
|
|
240
|
+
prune_stale_maps(path.dirname(file_path))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Every worker writes its own files into the run's directory; this reads them all back.
|
|
244
|
+
function collect_records(directory: string): Array<ReadRecords> {
|
|
245
|
+
return readdirSync(directory)
|
|
246
|
+
.filter((name) => name.endsWith(RECORD_EXTENSION))
|
|
247
|
+
.map((name) => parse_records(read_text(path.join(directory, name)) ?? ''))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const test_read_map = {
|
|
251
|
+
MAP_VERSION,
|
|
252
|
+
changed_since_tree,
|
|
253
|
+
collect_records,
|
|
254
|
+
in_unread_directory,
|
|
255
|
+
map_path,
|
|
256
|
+
merge_records,
|
|
257
|
+
parse_map,
|
|
258
|
+
read_map,
|
|
259
|
+
readers_of,
|
|
260
|
+
write_map,
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export type { ReadMap }
|
|
264
|
+
export { test_read_map }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { input_fingerprint } from '#scripts/gate/input-fingerprint'
|
|
5
|
+
import { changed_paths } from '#scripts/git/changed-paths'
|
|
6
|
+
import { git_command } from '#scripts/git/git-command'
|
|
7
|
+
import { git_spawn } from '#scripts/git/git-spawn'
|
|
8
|
+
import { test_read_map, type ReadMap } from './test-read-map'
|
|
9
|
+
import { test_read_trace } from './test-read-trace'
|
|
10
|
+
|
|
11
|
+
// When a unit run records the test-read map, and how the records become one. **Only a run of the
|
|
12
|
+
// whole suite records**: a map is read as "nothing else reads this file", which a narrowed, sharded
|
|
13
|
+
// or filtered run cannot vouch for. **Only a green run replaces the map**: a failed test may have
|
|
14
|
+
// stopped before the read it would have made.
|
|
15
|
+
|
|
16
|
+
type RecordingEnvironment = Record<string, string>
|
|
17
|
+
|
|
18
|
+
const RUN_SUBCOMMAND = 'run'
|
|
19
|
+
|
|
20
|
+
// The one flag a whole-suite run still carries — the gate's worker cap, which sizes the run without
|
|
21
|
+
// narrowing it.
|
|
22
|
+
const WORKER_FLAG_PREFIX = '--maxWorkers'
|
|
23
|
+
|
|
24
|
+
const RECORD_DIRECTORY_PREFIX = 'josh-test-reads-'
|
|
25
|
+
|
|
26
|
+
function is_recordable(vitest_arguments: ReadonlyArray<string>): boolean {
|
|
27
|
+
const [subcommand, ...flags] = vitest_arguments
|
|
28
|
+
|
|
29
|
+
return subcommand === RUN_SUBCOMMAND && flags.every((flag) => flag.startsWith(WORKER_FLAG_PREFIX))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Every directory the commit holds, NUL-separated so a name git would quote reads as itself.
|
|
33
|
+
async function commit_directories(commit: string): Promise<Array<string>> {
|
|
34
|
+
const output = await git_spawn.read(['ls-tree', '-r', '-d', '--name-only', '-z', commit])
|
|
35
|
+
|
|
36
|
+
return output.split('\0').filter((listed) => listed.length > 0)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type TreeSnapshot = Pick<ReadMap, 'commit' | 'dirty' | 'directories'>
|
|
40
|
+
|
|
41
|
+
// The tree the run read: the commit, every file that differed from it with its content's digest, and
|
|
42
|
+
// the directories it holds. **Read before the run starts**: a digest taken after it would vouch for
|
|
43
|
+
// an edit made while the tests ran as content they read.
|
|
44
|
+
async function tree_snapshot(root: string): Promise<TreeSnapshot> {
|
|
45
|
+
const commit = await git_command.head_commit()
|
|
46
|
+
const dirty = input_fingerprint.of_files(root, await changed_paths.read_changed_since(commit))
|
|
47
|
+
|
|
48
|
+
return { commit, dirty, directories: await commit_directories(commit) }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// A map that cannot be written — no git, a read-only common directory — leaves the previous map in
|
|
52
|
+
// place, and a narrowing over a stale map is refused by the commit it names.
|
|
53
|
+
function save_map(root: string, directory: string, snapshot: TreeSnapshot): void {
|
|
54
|
+
try {
|
|
55
|
+
const readers = test_read_map.merge_records(test_read_map.collect_records(directory))
|
|
56
|
+
|
|
57
|
+
test_read_map.write_map(root, { version: test_read_map.MAP_VERSION, ...snapshot, readers })
|
|
58
|
+
} catch {
|
|
59
|
+
// The previous map stays; the run's own result is unaffected.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function with_read_recording(
|
|
64
|
+
root: string,
|
|
65
|
+
vitest_arguments: ReadonlyArray<string>,
|
|
66
|
+
run: (environment: RecordingEnvironment) => Promise<number>,
|
|
67
|
+
): Promise<number> {
|
|
68
|
+
if (!is_recordable(vitest_arguments)) return await run({})
|
|
69
|
+
|
|
70
|
+
const directory = mkdtempSync(path.join(tmpdir(), RECORD_DIRECTORY_PREFIX))
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// Settled rather than awaited, so a tree git cannot read records no map and fails no run.
|
|
74
|
+
const [tree] = await Promise.allSettled([tree_snapshot(root)])
|
|
75
|
+
const exit_code = await run({
|
|
76
|
+
[test_read_trace.RECORD_DIRECTORY_KEY]: directory,
|
|
77
|
+
[test_read_trace.ROOT_KEY]: root,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (exit_code === 0 && tree.status === 'fulfilled') save_map(root, directory, tree.value)
|
|
81
|
+
|
|
82
|
+
return exit_code
|
|
83
|
+
} finally {
|
|
84
|
+
rmSync(directory, { recursive: true, force: true })
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const test_read_recording = { is_recordable, with_read_recording }
|
|
89
|
+
|
|
90
|
+
export type { RecordingEnvironment }
|
|
91
|
+
export { test_read_recording }
|