@joshuafolkken/kit 1.1784.0 → 1.2116.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/reviewer-verification.md +19 -0
- package/.claude/agents/reviewer.md +19 -0
- package/.claude/settings.json +5 -6
- package/.claude/skills/dependency-update/SKILL.md +9 -10
- package/.claude/skills/workflow-commands/SKILL.md +6 -6
- package/.claude/skills/workflow-commands/background-commands.md +8 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +41 -32
- package/.claude/skills/workflow-commands/delegation.md +6 -6
- package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +7 -5
- package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +9 -4
- package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -2
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
- package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
- package/.claude/skills/workflow-commands/prerequisite.md +3 -2
- package/.claude/skills/workflow-commands/retrospective.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +7 -5
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-pnpm/action.yml +9 -16
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/auto-tag.yml +45 -0
- package/.github/workflows/ci.yml +155 -15
- package/.github/workflows/dependabot-auto-merge.yml +63 -4
- package/.github/workflows/production.yml +8 -0
- package/.github/workflows/publish.yml +60 -45
- package/.vscode/settings.json +19 -0
- package/CLAUDE.md +5 -12
- package/SECURITY.md +2 -2
- package/dist/CLAUDE.md +5 -12
- package/dist/commands/document-read-cli.js +2 -1
- package/dist/commands/document-section-main.js +2 -1
- package/dist/commands/read-set-cli.js +194 -134
- package/dist/commands/run-prep-main.js +9694 -10173
- package/dist/commands/run-step-cli.js +22138 -18285
- package/dist/hooks/audit-provision.js +23 -5
- package/dist/hooks/chunk-3GKE653K.js +6204 -0
- package/dist/hooks/chunk-45CJJOO7.js +1243 -0
- package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
- package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
- package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
- package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
- package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
- package/dist/hooks/chunk-VVNYJWEI.js +30 -0
- package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
- package/dist/hooks/codex-hook-adapter.js +8 -8
- package/dist/hooks/format-edited.js +4 -5
- package/dist/hooks/inputs.json +386 -360
- package/dist/hooks/pretool-guard.js +7 -6
- package/dist/hooks/session-lang.js +101 -258
- package/dist/hooks/stop-guard.js +1744 -2576
- package/dist/josh.js +7133 -6368
- package/dist/version/index.js +161 -1
- package/eslint/rules/comment-why-only.js +108 -0
- package/package.json +16 -9
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +14 -11
- package/prompts/collaboration-workflow/issue-citation.md +0 -4
- package/prompts/collaboration-workflow/issue-template.md +40 -21
- package/prompts/collaboration-workflow/lane-instructions.md +3 -1
- package/prompts/collaboration-workflow/measurement.md +8 -1
- package/prompts/collaboration-workflow/operating-rules.md +11 -2
- package/prompts/collaboration-workflow/output-bounds.md +6 -2
- package/prompts/collaboration-workflow/plan-comment.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/residency.md +3 -2
- package/prompts/collaboration-workflow/rule-delivery.md +13 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +3 -1
- package/prompts/review-rubric.md +8 -0
- package/prompts/review.md +31 -22
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-argv.ts +14 -16
- package/scripts/agent/agent-provider.ts +138 -0
- package/scripts/agent/agent-role-profile.ts +108 -156
- package/scripts/agent/agent-safe-text.ts +20 -5
- package/scripts/agent/api-usage-limit.ts +65 -0
- package/scripts/agent/claude-agent-argv.ts +19 -9
- package/scripts/agent/codex-agent-argv.ts +118 -22
- package/scripts/agent/usage-limit-reset.ts +179 -0
- package/scripts/auth/claude-token-output.ts +2 -2
- package/scripts/auto-ok/auto-ok-author.ts +53 -0
- package/scripts/auto-ok/auto-ok-cli.ts +20 -9
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +6 -1
- package/scripts/backlog/backlog-drive-hold.ts +3 -3
- package/scripts/backlog/backlog-drive-idle.ts +10 -0
- package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
- package/scripts/backlog/backlog-drive-launch.ts +8 -2
- package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
- package/scripts/backlog/backlog-drive-named.ts +28 -7
- package/scripts/backlog/backlog-drive-paused.ts +5 -4
- package/scripts/backlog/backlog-drive-restore.ts +11 -3
- package/scripts/backlog/backlog-drive.ts +21 -60
- package/scripts/backlog/backlog-gate-scope.ts +61 -0
- package/scripts/backlog/backlog-measure-close.ts +112 -0
- package/scripts/backlog/backlog-next.ts +39 -49
- package/scripts/backlog/backlog-occupied.ts +2 -2
- package/scripts/backlog/backlog-overlap.ts +42 -8
- package/scripts/backlog/backlog-plan-cli.ts +4 -1
- package/scripts/backlog/backlog-plan-comment.ts +81 -0
- package/scripts/backlog/backlog-plan-read.ts +19 -6
- package/scripts/backlog/backlog-pool.ts +50 -12
- package/scripts/backlog/backlog-rank.ts +60 -13
- package/scripts/backlog/backlog-stalled-detect.ts +2 -1
- package/scripts/backlog/backlog-waves.ts +17 -2
- package/scripts/backlog/backlogrun-launch.ts +7 -4
- package/scripts/cost-runtime/context-cut-payback.ts +27 -25
- package/scripts/cost-runtime/cost-cli.ts +37 -16
- package/scripts/cost-runtime/cost-pricing.ts +9 -0
- package/scripts/cost-runtime/cost-verdict.ts +4 -1
- package/scripts/delegation/delegation-policy.ts +2 -2
- package/scripts/delegation/duplicate-read-guard.ts +1 -0
- package/scripts/delegation/investigation-guard.ts +14 -2
- package/scripts/delegation/investigation-reads.ts +45 -23
- package/scripts/dependabot/dependabot-cooldown.ts +3 -2
- package/scripts/discovery/repo-party.ts +80 -20
- package/scripts/discovery/session-owner-pin.ts +51 -0
- package/scripts/document/capped-print.ts +1 -1
- package/scripts/document/command-reference-line-budget.ts +42 -0
- package/scripts/document/document-byte-budget.ts +17 -7
- package/scripts/document/document-checks.ts +20 -0
- package/scripts/document/document-ratchet-check.ts +101 -0
- package/scripts/document/document-section.ts +1 -0
- package/scripts/document/entry-read-budget.ts +7 -3
- package/scripts/document/issue-citation-budget.ts +94 -0
- package/scripts/epic/epic-cli-argv.ts +2 -2
- package/scripts/epic/epic-fetch.ts +4 -2
- package/scripts/epic/epic-graph.ts +7 -2
- package/scripts/epic/epic-issue.ts +3 -1
- package/scripts/epic/epic-rank.ts +5 -0
- package/scripts/exports/unused-members-check.ts +62 -0
- package/scripts/exports/unused-members-cli.ts +6 -51
- package/scripts/followup/followup-ci-path.ts +67 -0
- package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
- package/scripts/followup/followup-coderabbit-wait.ts +13 -10
- package/scripts/followup/followup-failed-tests.ts +160 -0
- package/scripts/followup/followup-job-needs.ts +100 -0
- package/scripts/followup/followup-job-rerun.ts +303 -0
- package/scripts/followup/followup-main-move.ts +113 -0
- package/scripts/followup/git-followup-finish.ts +43 -11
- package/scripts/followup/git-followup-pending.ts +3 -3
- package/scripts/followup/git-followup-stages.ts +41 -11
- package/scripts/followup/git-pr-followup-checks.ts +55 -16
- package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
- package/scripts/followup/git-pr-followup.ts +62 -27
- package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
- package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
- package/scripts/gate/busy-average.ts +34 -2
- package/scripts/gate/check-related-pinned.ts +43 -0
- package/scripts/gate/check-related-reuse.ts +64 -0
- package/scripts/gate/check-related-section.ts +57 -0
- package/scripts/gate/check-related-unplanned.ts +34 -0
- package/scripts/gate/check-related.ts +265 -0
- package/scripts/gate/command-memory.ts +1 -1
- package/scripts/gate/core-admission.ts +24 -6
- package/scripts/gate/core-budget-timing.ts +20 -6
- package/scripts/gate/core-budget.ts +40 -11
- package/scripts/gate/core-wait-record.ts +3 -2
- package/scripts/gate/core-wait.ts +25 -2
- package/scripts/gate/core-weights.ts +15 -7
- package/scripts/gate/gate-labels.ts +46 -0
- package/scripts/gate/gate-ledger.ts +14 -6
- package/scripts/gate/gate-plan.ts +103 -58
- package/scripts/gate/gate-report.ts +9 -7
- package/scripts/gate/gate-run-steps.ts +26 -3
- package/scripts/gate/gate-skip.ts +45 -6
- package/scripts/gate/gate-static-group.ts +60 -0
- package/scripts/gate/gate-steps.ts +2 -1
- package/scripts/gate/gate-tree.ts +4 -2
- package/scripts/gate/hook-gate-reuse.ts +19 -2
- package/scripts/gate/metrics-totals.ts +59 -0
- package/scripts/gate/scoped-green.ts +68 -15
- package/scripts/gate/since-green.ts +14 -6
- package/scripts/gate/structural-checks.ts +96 -0
- package/scripts/gate/verification-gate.ts +51 -16
- package/scripts/gate/verification-inputs.ts +21 -6
- package/scripts/gate/verification-tail.ts +52 -0
- package/scripts/gh/gh-author-association.ts +23 -2
- package/scripts/gh/git-ai-review-scan.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +12 -3
- package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
- package/scripts/gh/git-gh-exec.ts +32 -2
- package/scripts/gh/git-gh-issue-read.ts +4 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +8 -2
- package/scripts/gh/git-gh-issue.ts +12 -5
- package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
- package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
- package/scripts/gh/git-gh-pr-read.ts +74 -13
- package/scripts/gh/git-gh-pr-rest.ts +3 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
- package/scripts/gh/git-gh-pr-too-large.ts +20 -0
- package/scripts/gh/git-gh-pr.ts +21 -7
- package/scripts/gh/git-gh-retry.ts +141 -0
- package/scripts/gh/git-pr-ai-review.ts +15 -6
- package/scripts/gh/git-pr-checks-eval.ts +3 -0
- package/scripts/gh/git-pr-checks.ts +5 -5
- package/scripts/git/base-tree.ts +30 -0
- package/scripts/git/change-base.ts +4 -10
- package/scripts/git/changed-paths-sync.ts +63 -0
- package/scripts/git/changed-paths.ts +16 -1
- package/scripts/git/git-common-directory.ts +11 -1
- package/scripts/git/git-diff-reads.ts +66 -20
- package/scripts/git/git-error.ts +9 -2
- package/scripts/git/git-schemas.ts +13 -0
- package/scripts/git/git-worktree.ts +13 -0
- package/scripts/git/gone-branch.ts +4 -1
- package/scripts/git/main-merge-budget-conflict.ts +89 -0
- package/scripts/git/main-merge-guard.ts +55 -18
- package/scripts/git/main-merge-regenerate.ts +59 -25
- package/scripts/git/main-merge.ts +53 -5
- package/scripts/git/no-base-checkout.ts +127 -0
- package/scripts/git/stash/git-stash.ts +11 -2
- package/scripts/git/stash/stash-push-cli.ts +10 -0
- package/scripts/hooks/batch-guard.ts +40 -21
- package/scripts/hooks/edited-cspell.ts +37 -11
- package/scripts/hooks/format-edited-file.ts +22 -2
- package/scripts/hooks/lane-plan-guard.ts +50 -0
- package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
- package/scripts/hooks/pre-commit-lint.ts +14 -1
- package/scripts/hooks/pre-push-related-switch.ts +10 -0
- package/scripts/hooks/pre-push-unit.ts +76 -10
- package/scripts/hooks/pretool-guard.ts +2 -0
- package/scripts/hooks/reserved-run.ts +14 -1
- package/scripts/hooks/run-hook.sh +23 -8
- package/scripts/hooks/step-zero-notice.ts +6 -1
- package/scripts/hooks/stop-actions.ts +3 -15
- package/scripts/hooks/stop-guard.ts +7 -6
- package/scripts/init/distributed-paths.ts +1 -0
- package/scripts/init/hook-command-rewrite.ts +5 -1
- package/scripts/init/hook-launch.ts +4 -2
- package/scripts/issue/aggregation-split.ts +52 -0
- package/scripts/issue/defect-origin-line.ts +19 -1
- package/scripts/issue/defect-rate-cli.ts +5 -102
- package/scripts/issue/defect-report.ts +4 -4
- package/scripts/issue/effect-audit-cli.ts +178 -0
- package/scripts/issue/effect-audit.ts +185 -0
- package/scripts/issue/expected-effect-lint.ts +64 -9
- package/scripts/issue/git-next-issues.ts +5 -0
- package/scripts/issue/issue-body-or-comment.ts +38 -4
- package/scripts/issue/issue-cite-cli.ts +1 -3
- package/scripts/issue/issue-classification.ts +1 -0
- package/scripts/issue/issue-closing-pr.ts +8 -1
- package/scripts/issue/issue-evidence-exit.ts +4 -2
- package/scripts/issue/issue-file-cap.ts +58 -0
- package/scripts/issue/issue-file-cli.ts +111 -38
- package/scripts/issue/issue-file-deferral.ts +52 -0
- package/scripts/issue/issue-file-fold.ts +29 -10
- package/scripts/issue/issue-file-interrupt.ts +85 -0
- package/scripts/issue/issue-file-lane-hint.ts +30 -0
- package/scripts/issue/issue-file.ts +43 -6
- package/scripts/issue/issue-labels.ts +19 -2
- package/scripts/issue/issue-lint-cli.ts +27 -2
- package/scripts/issue/issue-measured-cli.ts +13 -35
- package/scripts/issue/issue-measured-close.ts +58 -0
- package/scripts/issue/issue-not-before-label.ts +2 -2
- package/scripts/issue/issue-not-before.ts +4 -1
- package/scripts/issue/issue-priority-cli.ts +60 -0
- package/scripts/issue/issue-priority-raise.ts +55 -0
- package/scripts/issue/issue-priority.ts +174 -43
- package/scripts/issue/issue-rank.ts +21 -4
- package/scripts/issue/issue-read-cli.ts +32 -6
- package/scripts/issue/issue-read.ts +37 -12
- package/scripts/issue/issue-sample-gate.ts +95 -74
- package/scripts/issue/issue-sample-prime.ts +127 -0
- package/scripts/issue/issue-sample-required.ts +57 -0
- package/scripts/issue/issue-sample-source.ts +12 -11
- package/scripts/issue/issue-search.ts +124 -0
- package/scripts/issue/issue-state.ts +13 -1
- package/scripts/issue/issue-template-section.ts +43 -0
- package/scripts/issue/issue-watch-diff.ts +31 -1
- package/scripts/issue/issue-watch-hook.ts +11 -2
- package/scripts/issue/issue-watch.ts +55 -12
- package/scripts/issue/markdown-section.ts +1 -0
- package/scripts/issue/measure-declaration.ts +121 -0
- package/scripts/issue/measure-rerun-cli.ts +7 -70
- package/scripts/issue/plan-consumers-section.ts +22 -1
- package/scripts/issue/reproduction-measure.ts +3 -1
- package/scripts/issue/size-estimate.ts +90 -0
- package/scripts/issue/trusted-measure.ts +84 -0
- package/scripts/josh/command-usage.ts +3 -2
- package/scripts/josh/durable-state-migrate.ts +105 -0
- package/scripts/josh/durable-state.ts +112 -0
- package/scripts/josh/josh-aliases.ts +95 -0
- package/scripts/josh/josh-command-map.ts +1 -82
- package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
- package/scripts/josh/josh-commands-ai.ts +5 -5
- package/scripts/josh/josh-commands-development.ts +59 -58
- package/scripts/josh/josh-commands-issue-write.ts +23 -4
- package/scripts/josh/josh-commands-issue.ts +32 -0
- package/scripts/josh/josh-commands-lane.ts +87 -4
- package/scripts/josh/josh-commands-lint.ts +16 -3
- package/scripts/josh/josh-commands-maintenance.ts +15 -0
- package/scripts/josh/josh-commands-review.ts +41 -6
- package/scripts/josh/josh-commands-ship.ts +56 -8
- package/scripts/josh/josh-commands-test-check.ts +80 -0
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/josh-environment-file.ts +43 -3
- package/scripts/josh/josh-in-process.ts +2 -2
- package/scripts/josh/josh-logic.ts +5 -3
- package/scripts/josh/josh-run.ts +19 -1
- package/scripts/josh/run-record-path.ts +15 -0
- package/scripts/josh/stamp-file.ts +11 -2
- package/scripts/lane/lane-adopt-cli.ts +98 -0
- package/scripts/lane/lane-adopt.ts +208 -0
- package/scripts/lane/lane-await.ts +5 -8
- package/scripts/lane/lane-baseline.ts +123 -0
- package/scripts/lane/lane-breakdown-cli.ts +64 -0
- package/scripts/lane/lane-breakdown-implement.ts +170 -0
- package/scripts/lane/lane-breakdown-laps.ts +107 -0
- package/scripts/lane/lane-breakdown-pinned.ts +76 -0
- package/scripts/lane/lane-breakdown-text.ts +145 -0
- package/scripts/lane/lane-breakdown.ts +239 -0
- package/scripts/lane/lane-check-stats.ts +29 -8
- package/scripts/lane/lane-child-invocation.ts +9 -2
- package/scripts/lane/lane-child-mark-keys.ts +12 -0
- package/scripts/lane/lane-child-marker.ts +20 -1
- package/scripts/lane/lane-ci-path-stats.ts +73 -0
- package/scripts/lane/lane-claim-stats.ts +84 -0
- package/scripts/lane/lane-close.ts +8 -3
- package/scripts/lane/lane-dispatch-log.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +2 -2
- package/scripts/lane/lane-gap-stats.ts +7 -6
- package/scripts/lane/lane-guard-policy.ts +7 -1
- package/scripts/lane/lane-implement-stats.ts +207 -0
- package/scripts/lane/lane-implement-subagents.ts +90 -0
- package/scripts/lane/lane-investigation-switch.ts +16 -0
- package/scripts/lane/lane-ledger-kind.ts +31 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
- package/scripts/lane/lane-ledger-schema.ts +119 -36
- package/scripts/lane/lane-ledger-write.ts +257 -0
- package/scripts/lane/lane-ledger.ts +31 -177
- package/scripts/lane/lane-load.ts +1 -1
- package/scripts/lane/lane-measure-cli.ts +176 -59
- package/scripts/lane/lane-net-read.ts +44 -0
- package/scripts/lane/lane-net-time.ts +212 -0
- package/scripts/lane/lane-open.ts +16 -7
- package/scripts/lane/lane-output.ts +2 -4
- package/scripts/lane/lane-overrun-prompt.ts +147 -0
- package/scripts/lane/lane-overrun.ts +274 -0
- package/scripts/lane/lane-paths.ts +24 -1
- package/scripts/lane/lane-pause-cli.ts +4 -1
- package/scripts/lane/lane-paused-spans.ts +97 -0
- package/scripts/lane/lane-plan-check.ts +207 -0
- package/scripts/lane/lane-plan-cli.ts +41 -0
- package/scripts/lane/lane-plan.ts +144 -0
- package/scripts/lane/lane-registry.ts +14 -1
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-round-one-stats.ts +51 -0
- package/scripts/lane/lane-sampler.ts +23 -4
- package/scripts/lane/lane-session-action.ts +93 -18
- package/scripts/lane/lane-session-broker.ts +49 -21
- package/scripts/lane/lane-session-reread-main.ts +14 -0
- package/scripts/lane/lane-session-reread.ts +51 -0
- package/scripts/lane/lane-ship-lap-stats.ts +74 -0
- package/scripts/lane/lane-sleep.ts +58 -0
- package/scripts/lane/lane-spent.ts +1 -1
- package/scripts/lane/lane-split-stats.ts +28 -0
- package/scripts/lane/lane-stage-stats.ts +13 -1
- package/scripts/lane/lane-state.ts +41 -9
- package/scripts/lane/lane-stats-tables.ts +58 -0
- package/scripts/lane/lane-stats.ts +21 -5
- package/scripts/lane/lane-stop-stats.ts +51 -19
- package/scripts/lane/lane-tail-stats.ts +32 -0
- package/scripts/lane/lane-transcript-calls.ts +67 -10
- package/scripts/lane/lane-transcript-stats.ts +44 -12
- package/scripts/lane/lane-value-cell.ts +72 -0
- package/scripts/lane/lane-value.ts +118 -0
- package/scripts/lane/lane-withdraw-closed.ts +124 -0
- package/scripts/lane/lane-withdraw.ts +108 -31
- package/scripts/lane/lane-yield.ts +85 -0
- package/scripts/lane/openai-lane-ship.ts +12 -11
- package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
- package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
- package/scripts/lane/openai-lane-supervisor.ts +5 -4
- package/scripts/lib/poll.ts +38 -9
- package/scripts/lib/settled-together.ts +10 -0
- package/scripts/lib/shell-quote.ts +21 -0
- package/scripts/lib/token-shape.ts +111 -0
- package/scripts/lint/eslint-daemon-sources.ts +79 -0
- package/scripts/lint/eslint-daemon.ts +45 -6
- package/scripts/lint/eslint-warm-cli.ts +7 -0
- package/scripts/lint/eslint-warm.ts +66 -0
- package/scripts/lint/import-graph.ts +141 -11
- package/scripts/lint/lint-gate-scope.ts +2 -2
- package/scripts/lint/lint-parallel.ts +54 -6
- package/scripts/lint/lint-related.ts +154 -33
- package/scripts/lint/rename-cli.ts +179 -0
- package/scripts/lint/rename-identifier.ts +192 -0
- package/scripts/lint/type-only-imports.ts +58 -0
- package/scripts/notify/telegram-notify.ts +12 -3
- package/scripts/observations/observation-record.ts +42 -16
- package/scripts/observations/observations-flush.ts +32 -9
- package/scripts/overrides/overrides-check.ts +9 -1
- package/scripts/release/release-approval-prune.ts +7 -1
- package/scripts/repo/required-checks-logic.ts +4 -1
- package/scripts/repo/ruleset-check.ts +4 -3
- package/scripts/report/markdown-shape.ts +28 -0
- package/scripts/report/report-format-reference.ts +15 -1
- package/scripts/report/report-labels.ts +12 -1
- package/scripts/report/report-lint-child-summary.ts +94 -0
- package/scripts/report/report-lint-cli.ts +26 -7
- package/scripts/report/report-lint.ts +2 -20
- package/scripts/review/prose-stale-cli.ts +88 -0
- package/scripts/review/prose-stale-scan.ts +152 -0
- package/scripts/review/review-attest.ts +73 -23
- package/scripts/review/review-brief-cli.ts +68 -19
- package/scripts/review/review-brief-paths.ts +22 -0
- package/scripts/review/review-brief.ts +14 -7
- package/scripts/review/review-comment-carry.ts +47 -0
- package/scripts/review/review-comment-round.ts +85 -22
- package/scripts/review/review-finding-ledger.ts +109 -7
- package/scripts/review/review-finding-text.ts +43 -0
- package/scripts/review/review-interaction.ts +11 -0
- package/scripts/review/review-lane-handover.ts +68 -0
- package/scripts/review/review-lint-cli.ts +57 -0
- package/scripts/review/review-lint-findings.ts +92 -0
- package/scripts/review/review-lint-sections.ts +80 -0
- package/scripts/review/review-lint.ts +221 -0
- package/scripts/review/review-prior-findings.ts +102 -0
- package/scripts/review/review-readers-scan.ts +4 -2
- package/scripts/review/review-record-cli.ts +177 -54
- package/scripts/review/review-record-code-round.ts +55 -0
- package/scripts/review/review-record-comment.ts +60 -0
- package/scripts/review/review-record-interaction.ts +35 -0
- package/scripts/review/review-record-pinned.ts +108 -0
- package/scripts/review/review-record.ts +25 -1
- package/scripts/review/review-reviewed-tree.ts +307 -0
- package/scripts/review/review-round-one-closure.ts +62 -0
- package/scripts/review/review-round-reuse.ts +106 -0
- package/scripts/review/review-round2-cli.ts +91 -29
- package/scripts/review/review-round2-skip-comment.ts +55 -0
- package/scripts/review/review-round2.ts +24 -8
- package/scripts/review/review-stamps.ts +7 -3
- package/scripts/review/review-stop-log.ts +20 -6
- package/scripts/review/review-stops-cli.ts +2 -1
- package/scripts/review/review-submit-cli.ts +101 -0
- package/scripts/rules/agent-model.ts +1 -1
- package/scripts/rules/bash-triggers.ts +3 -3
- package/scripts/rules/concise-reply.ts +88 -0
- package/scripts/rules/credential-body.ts +77 -0
- package/scripts/rules/decision-oracle.ts +1 -1
- package/scripts/rules/delivered-rules.ts +36 -17
- package/scripts/rules/destructive-command.ts +3 -11
- package/scripts/rules/direct-issue-comment.ts +34 -12
- package/scripts/rules/direct-issue-read.ts +191 -0
- package/scripts/rules/direct-pr-create.ts +4 -24
- package/scripts/rules/early-heartbeat.ts +2 -0
- package/scripts/rules/edit-tool.ts +27 -0
- package/scripts/rules/file-edit-rows.ts +17 -0
- package/scripts/rules/file-rewrite.ts +146 -0
- package/scripts/rules/firing-point.ts +3 -2
- package/scripts/rules/gh-api.ts +128 -7
- package/scripts/rules/gh-repo-selector.ts +84 -0
- package/scripts/rules/git-force.ts +2 -2
- package/scripts/rules/guard-remedy.ts +14 -5
- package/scripts/rules/implementation-cut.ts +61 -28
- package/scripts/rules/investigator-foreground.ts +231 -0
- package/scripts/rules/issue-citation.ts +15 -60
- package/scripts/rules/issue-comments.ts +9 -4
- package/scripts/rules/josh-git-bare.ts +1 -1
- package/scripts/rules/lane-background.ts +41 -13
- package/scripts/rules/lane-direct-tool.ts +107 -9
- package/scripts/rules/lane-interactive-ask.ts +4 -1
- package/scripts/rules/lane-park.ts +4 -2
- package/scripts/rules/lane-spilled-read.ts +90 -0
- package/scripts/rules/lane-split-park.ts +1 -1
- package/scripts/rules/lane-switch-main.ts +4 -4
- package/scripts/rules/last-prompt.ts +20 -47
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/permission-guards.ts +5 -1
- package/scripts/rules/piped-verification-shapes.ts +110 -0
- package/scripts/rules/piped-verification.ts +67 -17
- package/scripts/rules/poll-loop.ts +72 -9
- package/scripts/rules/pr-merge.ts +5 -5
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +88 -24
- package/scripts/rules/reply-language.ts +3 -17
- package/scripts/rules/reply-prose.ts +34 -0
- package/scripts/rules/repo-admin.ts +132 -0
- package/scripts/rules/reviewer-foreground.ts +88 -0
- package/scripts/rules/round-one-check-related.ts +120 -0
- package/scripts/rules/rule-body-guard.ts +5 -15
- package/scripts/rules/rule-value-cache.ts +53 -5
- package/scripts/rules/rule-value-candidates.ts +84 -0
- package/scripts/rules/rule-value-cli.ts +59 -11
- package/scripts/rules/rule-value.ts +17 -4
- package/scripts/rules/run-tail-rule.ts +1 -1
- package/scripts/rules/sed-in-place.ts +196 -0
- package/scripts/rules/shell-segments.ts +10 -2
- package/scripts/rules/stop-rule-rows.ts +1 -13
- package/scripts/rules/stop-rules.ts +33 -58
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +110 -23
- package/scripts/rules/timed-rerun.ts +1 -1
- package/scripts/rules/turn-filings.ts +90 -0
- package/scripts/rules/worktree-guard.ts +22 -7
- package/scripts/run/board/run-board-ending-reason.ts +6 -2
- package/scripts/run/board/run-board-fit.ts +24 -1
- package/scripts/run/board/run-board-found.ts +134 -0
- package/scripts/run/board/run-board-header.ts +8 -0
- package/scripts/run/board/run-board-labels.ts +11 -1
- package/scripts/run/board/run-board-lane-time.ts +1 -0
- package/scripts/run/board/run-board-link.ts +15 -2
- package/scripts/run/board/run-board-notes.ts +7 -1
- package/scripts/run/board/run-board-operation.ts +4 -2
- package/scripts/run/board/run-board-phase.ts +2 -5
- package/scripts/run/board/run-board-plan.ts +1 -1
- package/scripts/run/board/run-board-read.ts +46 -1
- package/scripts/run/board/run-board-reason.ts +20 -8
- package/scripts/run/board/run-board-render-checks.ts +3 -2
- package/scripts/run/board/run-board-render-drawn.ts +11 -0
- package/scripts/run/board/run-board-render-found.ts +157 -0
- package/scripts/run/board/run-board-render-lead.ts +9 -0
- package/scripts/run/board/run-board-render-legend.ts +19 -0
- package/scripts/run/board/run-board-render-tail.ts +1 -1
- package/scripts/run/board/run-board-render.ts +82 -18
- package/scripts/run/board/run-board-spin.ts +1 -3
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +39 -10
- package/scripts/run/carry/run-carry-ended.ts +3 -2
- package/scripts/run/carry/run-carry.ts +3 -2
- package/scripts/run/cut/run-cut-cli.ts +33 -133
- package/scripts/run/cut/run-cut-digest.ts +152 -0
- package/scripts/run/cut/run-cut-handoff.ts +52 -8
- package/scripts/run/cut/run-cut-report.ts +24 -3
- package/scripts/run/cut/run-cut-resume.ts +129 -0
- package/scripts/run/cut/run-cut-review.ts +105 -0
- package/scripts/run/cut/run-cut.ts +18 -7
- package/scripts/run/detached-launch.ts +1 -1
- package/scripts/run/entry/run-entry-cli.ts +88 -19
- package/scripts/run/entry/run-entry-spill.ts +83 -0
- package/scripts/run/entry/run-entry-stop.ts +5 -1
- package/scripts/run/entry/run-entry-trust.ts +91 -0
- package/scripts/run/event/run-event-baseline.ts +56 -0
- package/scripts/run/event/run-event-filed.ts +9 -3
- package/scripts/run/event/run-event-overrun.ts +197 -0
- package/scripts/run/event/run-event-render.ts +4 -0
- package/scripts/run/event/run-event-stream.ts +25 -3
- package/scripts/run/hold/run-stop-notify.ts +3 -8
- package/scripts/run/merge/run-merge-cap.ts +57 -0
- package/scripts/run/merge/run-merge-cli.ts +66 -23
- package/scripts/run/merge/run-merge-outcome.ts +2 -0
- package/scripts/run/merge/run-merge-ship.ts +13 -3
- package/scripts/run/merge/run-merge-signals.ts +40 -23
- package/scripts/run/merge/run-merge-steps.ts +56 -36
- package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
- package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
- package/scripts/run/merge/run-merge.ts +41 -14
- package/scripts/run/review/run-review-cli.ts +306 -20
- package/scripts/run/review/run-review-early-push.ts +121 -0
- package/scripts/run/review/run-review-join-read.ts +94 -0
- package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
- package/scripts/run/review/run-review-pinned-join.ts +216 -0
- package/scripts/run/review/run-review-pinned-steps.ts +383 -0
- package/scripts/run/review/run-review-pinned-tree.ts +37 -0
- package/scripts/run/review/run-review-pinned.ts +53 -0
- package/scripts/run/review/run-review-steps.ts +70 -34
- package/scripts/run/review/run-review.ts +26 -6
- package/scripts/run/run-parent-cut-hook.ts +3 -4
- package/scripts/run/run-prep.ts +1 -0
- package/scripts/run/run-stage-read.ts +2 -6
- package/scripts/run/run-step-cli.ts +17 -5
- package/scripts/run/run-step-lane-plan.ts +27 -0
- package/scripts/run/run-step-section.ts +26 -7
- package/scripts/run/run-step.ts +5 -2
- package/scripts/run/ship/run-ship-args.ts +19 -0
- package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
- package/scripts/run/ship/run-ship-cli.ts +131 -39
- package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
- package/scripts/run/ship/run-ship-detach.ts +10 -4
- package/scripts/run/ship/run-ship-handover.ts +106 -0
- package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
- package/scripts/run/ship/run-ship-lap.ts +63 -0
- package/scripts/run/ship/run-ship-next.ts +27 -26
- package/scripts/run/ship/run-ship-overlap.ts +8 -3
- package/scripts/run/ship/run-ship-place.ts +9 -2
- package/scripts/run/ship/run-ship-planned.ts +92 -0
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
- package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
- package/scripts/run/ship/run-ship-preflight.ts +16 -4
- package/scripts/run/ship/run-ship-probe.ts +23 -9
- package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
- package/scripts/run/ship/run-ship-relaunch.ts +51 -0
- package/scripts/run/ship/run-ship-return.ts +103 -29
- package/scripts/run/ship/run-ship-review-reason.ts +24 -0
- package/scripts/run/ship/run-ship-review-steps.ts +230 -59
- package/scripts/run/ship/run-ship-review.ts +171 -51
- package/scripts/run/ship/run-ship-reviewer.ts +138 -42
- package/scripts/run/ship/run-ship-scoped.ts +39 -10
- package/scripts/run/ship/run-ship-stage.ts +49 -13
- package/scripts/run/ship/run-ship-steps.ts +24 -4
- package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
- package/scripts/run/ship/run-ship-stop-context.ts +50 -13
- package/scripts/run/ship/run-ship-sync.ts +171 -30
- package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
- package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
- package/scripts/run/ship/run-ship-waiter.ts +114 -0
- package/scripts/run/ship/run-ship.ts +16 -2
- package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
- package/scripts/run/wake/run-wake-cli.ts +6 -113
- package/scripts/run/wake/run-wake-code.ts +53 -11
- package/scripts/run/wake/run-wake-driver.ts +5 -3
- package/scripts/run/wake/run-wake-finish.ts +137 -0
- package/scripts/run/wake/run-wake-loop.ts +5 -3
- package/scripts/run/wake/run-wake-ports.ts +11 -3
- package/scripts/run/wake/run-wake-session.ts +2 -0
- package/scripts/run/wake/run-wake.ts +3 -2
- package/scripts/safe-chain/ci-installer-pin.ts +49 -8
- package/scripts/safe-chain/project-config.ts +3 -2
- package/scripts/security/secretlint-scan.ts +42 -9
- package/scripts/security/security-audit-provision-main.ts +9 -1
- package/scripts/security/security-audit-provision.ts +1 -1
- package/scripts/security/security-audit.ts +21 -1
- package/scripts/security/sonar-file.ts +10 -5
- package/scripts/split/split-assess.ts +12 -3
- package/scripts/split/split-early-notice.ts +116 -0
- package/scripts/split/split-measure.ts +20 -2
- package/scripts/split/split-resolution.ts +107 -0
- package/scripts/test/direct-importers.ts +185 -0
- package/scripts/test/pure-files-entries.ts +83 -0
- package/scripts/test/related-heavy-tests.ts +81 -0
- package/scripts/test/test-no-base.ts +88 -0
- package/scripts/test/test-pinned-cache-logic.ts +363 -0
- package/scripts/test/test-pinned-cache.ts +337 -0
- package/scripts/test/test-pinned-emit.ts +49 -0
- package/scripts/test/test-pinned-functions.ts +116 -0
- package/scripts/test/test-pinned-ledger.ts +80 -0
- package/scripts/test/test-pinned-logic.ts +208 -20
- package/scripts/test/test-pinned-main.ts +84 -18
- package/scripts/test/test-pinned-pool.ts +73 -0
- package/scripts/test/test-pinned-probe.ts +254 -0
- package/scripts/test/test-pinned-prune-logic.ts +118 -0
- package/scripts/test/test-pinned-reach.ts +194 -0
- package/scripts/test/test-pinned-record.ts +196 -0
- package/scripts/test/test-pinned-rewire.ts +148 -0
- package/scripts/test/test-pinned-spawn.ts +81 -0
- package/scripts/test/test-pinned-stage.ts +70 -0
- package/scripts/test/test-pinned.ts +258 -47
- package/scripts/test/test-reach-cli.ts +61 -0
- package/scripts/test/test-read-map.ts +35 -11
- package/scripts/test/test-read-recording.ts +17 -5
- package/scripts/test/test-read-trace.ts +26 -8
- package/scripts/test/test-red-logic.ts +46 -7
- package/scripts/test/test-red.ts +34 -14
- package/scripts/test/test-related-claim.ts +37 -0
- package/scripts/test/test-related-main.ts +4 -1
- package/scripts/test/test-related-moved.ts +91 -0
- package/scripts/test/test-related-record.ts +43 -23
- package/scripts/test/test-related-scope.ts +99 -34
- package/scripts/test/test-related-synced.ts +135 -0
- package/scripts/test/test-related-touched.ts +84 -0
- package/scripts/test/test-related.ts +167 -83
- package/scripts/test/test-unit-guard.ts +28 -5
- package/scripts/test/unit-durations.ts +41 -0
- package/scripts/test/unit-worker-share.ts +5 -4
- package/scripts/test/vitest-cache-directory.ts +19 -0
- package/scripts/test/vitest-cli-excludes.ts +14 -0
- package/scripts/test/vitest-failure-summary.ts +111 -0
- package/scripts/test/whole-suite-files.ts +12 -1
- package/scripts/time-runtime/time-batch-guard.ts +42 -4
- package/scripts/time-runtime/time-bundle-call.ts +1 -1
- package/scripts/time-runtime/time-bundles.ts +36 -5
- package/scripts/time-runtime/time-markers.ts +2 -2
- package/scripts/time-runtime/time-search-call.ts +38 -0
- package/scripts/time-runtime/time-shell.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +17 -5
- package/scripts/version/development-engines-verdict.ts +124 -0
- package/scripts/version/latest-corepack.ts +2 -0
- package/templates/gitignore +2 -0
- package/templates/workflows/ci.yml +41 -0
- package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
- package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
- package/dist/hooks/chunk-TUIFS5E7.js +0 -72
- package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
- package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
- package/scripts/rules/filing-cap.ts +0 -139
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { git_spawn } from '#scripts/git/git-spawn'
|
|
4
|
+
import { import_graph, type ImportGraph } from '#scripts/lint/import-graph'
|
|
5
|
+
import { test_declared_logic } from './test-declared-logic'
|
|
6
|
+
import { test_pinned_cache_logic } from './test-pinned-cache-logic'
|
|
7
|
+
import { test_pinned_spawn, type TestSpawn } from './test-pinned-spawn'
|
|
8
|
+
import { related_scope } from './test-related-scope'
|
|
9
|
+
import { whole_suite_files } from './whole-suite-files'
|
|
10
|
+
|
|
11
|
+
// What one `josh test:pinned` verdict rests on, read off the import graph (joshuafolkken/kit#4873): the
|
|
12
|
+
// `basis` `test-pinned-cache-logic.ts` holds a verdict by past the tree-wide digests. The graph is built
|
|
13
|
+
// on the first basis asked for, so a rerun the tree-wide digests already hold builds none — unless a
|
|
14
|
+
// changed fixture asks `probed_files` for its reach.
|
|
15
|
+
//
|
|
16
|
+
// **Where no graph can be built (no `typescript`, no `tsconfig.json`) every basis is undefined**, and a
|
|
17
|
+
// verdict holds only by the tree-wide digests, as before.
|
|
18
|
+
|
|
19
|
+
const NUL = '\0'
|
|
20
|
+
const FIXTURE_MARK = '-fixture'
|
|
21
|
+
|
|
22
|
+
// `related` names the tests reaching a file, which `test-pinned-cache-logic.ts` turns into a basis.
|
|
23
|
+
interface Reach {
|
|
24
|
+
related: (file: string) => Promise<Array<string> | undefined>
|
|
25
|
+
failed: (tests: ReadonlyArray<string>) => Promise<string | undefined>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// What a basis is read against: the base commit, each changed non-test path with its content's digest,
|
|
29
|
+
// and each test file's.
|
|
30
|
+
interface ReachInputs {
|
|
31
|
+
commit: string
|
|
32
|
+
code: ReadonlyArray<string>
|
|
33
|
+
digests: ReadonlyMap<string, string>
|
|
34
|
+
test_digests: ReadonlyMap<string, string>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// One file and how many test files its imports reach.
|
|
38
|
+
interface ReachedCount {
|
|
39
|
+
file: string
|
|
40
|
+
count: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface GraphView {
|
|
44
|
+
graph: ImportGraph
|
|
45
|
+
universe: ReadonlyArray<string>
|
|
46
|
+
spawn: TestSpawn
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Every file in the work tree, tracked or untracked but not ignored. `-z` lists a path as it is,
|
|
50
|
+
// where `core.quotePath` would quote a non-ASCII one.
|
|
51
|
+
async function work_tree_files(): Promise<Array<string>> {
|
|
52
|
+
const listed = await git_spawn.read(['ls-files', '-coz', '--exclude-standard'])
|
|
53
|
+
|
|
54
|
+
return listed.split(NUL).filter((file) => file !== '')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Every code file the work tree holds, as absolute paths the graph resolves to.
|
|
58
|
+
async function code_universe(): Promise<Array<string>> {
|
|
59
|
+
const files = await work_tree_files()
|
|
60
|
+
const code = files.filter((file) => related_scope.is_relatable(file) && existsSync(file))
|
|
61
|
+
|
|
62
|
+
return code.map((file) => path.resolve(file))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function view_of(): Promise<GraphView | undefined> {
|
|
66
|
+
const graph = await import_graph.create(process.cwd())
|
|
67
|
+
if (graph === undefined) return undefined
|
|
68
|
+
|
|
69
|
+
const universe = await code_universe()
|
|
70
|
+
|
|
71
|
+
return { graph, universe, spawn: test_pinned_spawn.create(graph, universe) }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// A code file nothing imports that is not a test: a config, a setup file or a CLI a test spawns.
|
|
75
|
+
function is_root(view: GraphView, file: string): boolean {
|
|
76
|
+
const relative = path.relative(process.cwd(), file)
|
|
77
|
+
if (test_declared_logic.is_test_file(relative)) return false
|
|
78
|
+
|
|
79
|
+
return view.graph.importers([file], view.universe).size <= 1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// A changed path no graph ties to the tests that read it: a non-code file a test reads, or one every
|
|
83
|
+
// test depends on without importing it (the vitest config, the setup files, the lock).
|
|
84
|
+
function is_shared(file: string): boolean {
|
|
85
|
+
return (
|
|
86
|
+
!related_scope.is_relatable(file) ||
|
|
87
|
+
whole_suite_files.WHOLE_SUITE_FILES.has(path.normalize(file))
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// A shared path, or a code file a root reaches — the root itself or a module it imports, which a test
|
|
92
|
+
// spawning it runs past any closure.
|
|
93
|
+
function is_asset(view: GraphView, file: string): boolean {
|
|
94
|
+
if (is_shared(file)) return true
|
|
95
|
+
const reached = view.graph.runtime_importers([path.resolve(file)], view.universe)
|
|
96
|
+
|
|
97
|
+
return [...reached].some((reader) => is_root(view, reader))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The mark of the test-only modules `package.json`'s `files` keeps out of the published package.
|
|
101
|
+
function is_fixture(file: string): boolean {
|
|
102
|
+
return path.basename(file).includes(FIXTURE_MARK)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The changed files `josh test:pinned` probes (joshuafolkken/kit#4879): a fixture no root reaches only
|
|
106
|
+
// moves what its tests cover, so its hunks hold no runtime behavior a test could pin. One a root reaches is
|
|
107
|
+
// kept, and so is every fixture where no graph can be built; the graph is built only for a fixture.
|
|
108
|
+
async function probed_files(files: ReadonlyArray<string>): Promise<Array<string>> {
|
|
109
|
+
if (files.every((file) => !is_fixture(file))) return [...files]
|
|
110
|
+
const view = await view_of()
|
|
111
|
+
if (view === undefined) return [...files]
|
|
112
|
+
|
|
113
|
+
return files.filter((file) => !is_fixture(file) || is_asset(view, file))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// The test files whose runtime imports reach `file` — the graph vitest walks (joshuafolkken/kit#5368).
|
|
117
|
+
function related_of(view: GraphView, file: string): Array<string> {
|
|
118
|
+
const reached = view.graph.runtime_importers([path.resolve(file)], view.universe)
|
|
119
|
+
|
|
120
|
+
return [...reached]
|
|
121
|
+
.map((reader) => path.relative(process.cwd(), reader))
|
|
122
|
+
.filter((reader) => test_declared_logic.is_test_file(reader))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// How many test files the imports of `files` reach, a listed test counting itself — what
|
|
126
|
+
// `josh test:related` sizes its core claim by before vitest walks the same graph
|
|
127
|
+
// (joshuafolkken/kit#5135). Undefined where no graph can be built.
|
|
128
|
+
async function reached_test_count(files: ReadonlyArray<string>): Promise<number | undefined> {
|
|
129
|
+
const view = await view_of()
|
|
130
|
+
if (view === undefined) return undefined
|
|
131
|
+
|
|
132
|
+
return new Set(files.flatMap((file) => related_of(view, file))).size
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Each file's own `reached_test_count`, over one graph — what `josh test:reach` prints
|
|
136
|
+
// (joshuafolkken/kit#5238). Undefined where no graph can be built.
|
|
137
|
+
async function reached_test_counts(
|
|
138
|
+
files: ReadonlyArray<string>,
|
|
139
|
+
): Promise<Array<ReachedCount> | undefined> {
|
|
140
|
+
const view = await view_of()
|
|
141
|
+
if (view === undefined) return undefined
|
|
142
|
+
|
|
143
|
+
return files.map((file) => ({ file, count: related_of(view, file).length }))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// None where no failed test is named or one is gone: what such a red rests on, no graph names. Past
|
|
147
|
+
// the failed tests' closures it rests on the shared paths and on each file they can start, with its
|
|
148
|
+
// closure (joshuafolkken/kit#5261) — a root no failed test names moves no verdict of theirs.
|
|
149
|
+
function failed_of(
|
|
150
|
+
view: GraphView,
|
|
151
|
+
tests: ReadonlyArray<string>,
|
|
152
|
+
inputs: ReachInputs,
|
|
153
|
+
): string | undefined {
|
|
154
|
+
if (tests.length === 0 || tests.some((test) => !inputs.test_digests.has(test))) return undefined
|
|
155
|
+
const started = view.spawn.started(tests.map((test) => path.resolve(test)))
|
|
156
|
+
const run = [...tests, ...[...started].map((file) => path.relative(process.cwd(), file))]
|
|
157
|
+
const closures = run.map((file) => [file, view.graph.closure_hash(path.resolve(file))] as const)
|
|
158
|
+
const assets = inputs.code.filter((file) => is_shared(file) || started.has(path.resolve(file)))
|
|
159
|
+
const change = test_pinned_cache_logic.change_of(inputs.commit, assets, inputs.digests)
|
|
160
|
+
|
|
161
|
+
return test_pinned_cache_logic.red_basis(change, closures)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function create(inputs: ReachInputs): Reach {
|
|
165
|
+
const held: { view?: Promise<GraphView | undefined> } = {}
|
|
166
|
+
|
|
167
|
+
async function related(file: string): Promise<Array<string> | undefined> {
|
|
168
|
+
held.view ??= view_of()
|
|
169
|
+
const read = await held.view
|
|
170
|
+
|
|
171
|
+
return read === undefined ? undefined : related_of(read, file)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function failed(tests: ReadonlyArray<string>): Promise<string | undefined> {
|
|
175
|
+
held.view ??= view_of()
|
|
176
|
+
const read = await held.view
|
|
177
|
+
|
|
178
|
+
return read === undefined ? undefined : failed_of(read, tests, inputs)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { related, failed }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const test_pinned_reach = {
|
|
185
|
+
code_universe,
|
|
186
|
+
create,
|
|
187
|
+
probed_files,
|
|
188
|
+
reached_test_count,
|
|
189
|
+
reached_test_counts,
|
|
190
|
+
work_tree_files,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type { Reach, ReachedCount, ReachInputs }
|
|
194
|
+
export { test_pinned_reach }
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { gate_log } from '#scripts/gate/gate-log'
|
|
2
|
+
import type { KeptLog } from '#scripts/gate/verification-tail'
|
|
3
|
+
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
4
|
+
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
5
|
+
import { test_pinned_cache_logic } from './test-pinned-cache-logic'
|
|
6
|
+
import type { PinnedOrigin } from './test-pinned-ledger'
|
|
7
|
+
import { test_pinned_logic, type PinnedReport, type ReportParts } from './test-pinned-logic'
|
|
8
|
+
|
|
9
|
+
// The answer `josh test:pinned --record` leaves for `josh run:review --join` (joshuafolkken/kit#4770):
|
|
10
|
+
// the printed report and its unpinned count, keyed to the checkout. `run:review` starts the check in
|
|
11
|
+
// the background beside the gate and the brief, so the join — a later process — reads it here.
|
|
12
|
+
//
|
|
13
|
+
// **Each answer is filed under its launch's token**, so a read answers only for the token it asks
|
|
14
|
+
// with: a check an earlier start left running can still write after the next start cleared the record,
|
|
15
|
+
// and its answer — about a tree the join never asked about — lands in its own file rather than over
|
|
16
|
+
// this launch's. The token comes from the command line, so the file name carries its digest.
|
|
17
|
+
|
|
18
|
+
const RECORD_PREFIX = 'josh-test-pinned-record-'
|
|
19
|
+
const TOKEN_KEY_LENGTH = 16
|
|
20
|
+
const SITE_KEY_LENGTH = 16
|
|
21
|
+
const NOT_RUN_NOTE = '`josh test:pinned` could not run, so nothing was checked: '
|
|
22
|
+
const FIRST_UNPINNED_FLAG = '--first-unpinned'
|
|
23
|
+
const LIST_PREFIX = 'josh-test-pinned-list-'
|
|
24
|
+
const LIST_SUFFIX = '.log'
|
|
25
|
+
const LIST_HEADER = 'josh test:pinned — every unpinned and unchecked place'
|
|
26
|
+
|
|
27
|
+
type UnpinnedHunk = PinnedReport['unpinned'][number]
|
|
28
|
+
|
|
29
|
+
// `unpinned` holds one key per unpinned place (`site_of`); an identical place in the same file takes its
|
|
30
|
+
// occurrence number, so a copy added later is a place no record holds.
|
|
31
|
+
// `text`, `counts` and `log` are the printed report's parts (`test_pinned_logic.report_text`).
|
|
32
|
+
interface PinnedSummary extends ReportParts {
|
|
33
|
+
unpinned: ReadonlyArray<string>
|
|
34
|
+
// What a check still running reached so far; it is shown, never judged or remembered as the answer.
|
|
35
|
+
is_partial?: boolean
|
|
36
|
+
// A `--first-unpinned` check that stopped before some hunks (joshuafolkken/kit#5153): its unpinned
|
|
37
|
+
// places are not every one, so no reason accepts it as the whole answer.
|
|
38
|
+
is_cut?: boolean
|
|
39
|
+
// How many hunks a cut check did not probe, for the note on the probe that goes on with them
|
|
40
|
+
// (joshuafolkken/kit#5327).
|
|
41
|
+
remaining?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A place's key leaves out its line, so merging the default branch in, which moves the line, keeps the
|
|
45
|
+
// key (joshuafolkken/kit#4775): the file, the function, and a digest of the lines the change replaced
|
|
46
|
+
// and the lines it put in their place.
|
|
47
|
+
function site_of(hunk: UnpinnedHunk): string {
|
|
48
|
+
const body = JSON.stringify([hunk.new_count, hunk.old_lines, hunk.new_lines ?? []])
|
|
49
|
+
const key = stamp_file.digest(body).slice(0, SITE_KEY_LENGTH)
|
|
50
|
+
const place = hunk.function_name === undefined ? hunk.file : `${hunk.file} ${hunk.function_name}`
|
|
51
|
+
|
|
52
|
+
return `${place} ${key}`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// **The full list goes to a log once any place is folded** (joshuafolkken/kit#5188): the printed report
|
|
56
|
+
// counts the unchecked places by reason, so the list is kept where a `tail` cannot cut it.
|
|
57
|
+
function kept_log(report: PinnedReport, root: string): KeptLog | undefined {
|
|
58
|
+
if (!test_pinned_logic.has_unchecked(report)) return undefined
|
|
59
|
+
const output = test_pinned_logic.format(report)
|
|
60
|
+
const target = stamp_file.stamp_path(LIST_PREFIX, root, LIST_SUFFIX)
|
|
61
|
+
|
|
62
|
+
return { path: gate_log.write_gate_log([{ header: LIST_HEADER, output }], target) }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function summary_with(report: PinnedReport, log: KeptLog | undefined): PinnedSummary {
|
|
66
|
+
const summary = {
|
|
67
|
+
...test_pinned_logic.report_parts(report),
|
|
68
|
+
log,
|
|
69
|
+
unpinned: test_pinned_cache_logic.distinct(report.unpinned.map((hunk) => site_of(hunk))),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return report.cut === undefined
|
|
73
|
+
? summary
|
|
74
|
+
: { ...summary, is_cut: true, remaining: report.cut.length }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function summary_of(report: PinnedReport, root: string = PROJECT_ROOT): PinnedSummary {
|
|
78
|
+
return summary_with(report, kept_log(report, root))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// What a check still running has reached so far (joshuafolkken/kit#4936): `note` says the rest is to
|
|
82
|
+
// come. It keeps no list: the per-checkout list log is the finished check's, which its record names.
|
|
83
|
+
function partial_summary(report: PinnedReport, note: string): PinnedSummary {
|
|
84
|
+
const summary = summary_with(report, undefined)
|
|
85
|
+
|
|
86
|
+
return { ...summary, text: `${note}\n${summary.text}`, is_partial: true }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// A check that could not run checked nothing, so it names no unpinned place and the run fails open.
|
|
90
|
+
function not_run_summary(error: unknown): PinnedSummary {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
92
|
+
|
|
93
|
+
return { text: `${NOT_RUN_NOTE}${message}\n`, unpinned: [] }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function record_path(token: string, root: string = PROJECT_ROOT): string {
|
|
97
|
+
const key = stamp_file.digest(token).slice(0, TOKEN_KEY_LENGTH)
|
|
98
|
+
|
|
99
|
+
return stamp_file.stamp_path(`${RECORD_PREFIX}${key}-`, root)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function write(token: string, summary: PinnedSummary, root: string = PROJECT_ROOT): void {
|
|
103
|
+
stamp_file.write_stamp(record_path(token, root), { token, ...summary })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function is_text_list(value: unknown): value is Array<string> {
|
|
107
|
+
return Array.isArray(value) && value.every((item) => typeof item === 'string')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// A kept log's path, absent from the record when its write failed.
|
|
111
|
+
function log_of(value: unknown): KeptLog | undefined {
|
|
112
|
+
if (typeof value !== 'object' || value === null) return undefined
|
|
113
|
+
const kept = value as Record<string, unknown>
|
|
114
|
+
|
|
115
|
+
return { path: typeof kept['path'] === 'string' ? kept['path'] : undefined }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The counts line and the log: absent from a check that could not run.
|
|
119
|
+
function tail_of(record: Record<string, unknown>): Pick<PinnedSummary, 'counts' | 'log'> {
|
|
120
|
+
const { counts, log } = record
|
|
121
|
+
|
|
122
|
+
return typeof counts === 'string' ? { counts, log: log_of(log) } : {}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The count of hunks a cut check did not probe: absent from a record written before it was kept.
|
|
126
|
+
function remaining_of(record: Record<string, unknown>): Pick<PinnedSummary, 'remaining'> {
|
|
127
|
+
const { remaining } = record
|
|
128
|
+
|
|
129
|
+
return typeof remaining === 'number' ? { remaining } : {}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function to_summary(record: Record<string, unknown>): PinnedSummary | undefined {
|
|
133
|
+
const { text, unpinned, is_cut } = record
|
|
134
|
+
|
|
135
|
+
if (typeof text !== 'string' || !is_text_list(unpinned)) return undefined
|
|
136
|
+
const summary = { text, unpinned, ...tail_of(record) }
|
|
137
|
+
|
|
138
|
+
return is_cut === true ? { ...summary, is_cut, ...remaining_of(record) } : summary
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parsed(raw: string): Record<string, unknown> | undefined {
|
|
142
|
+
try {
|
|
143
|
+
const value: unknown = JSON.parse(raw)
|
|
144
|
+
|
|
145
|
+
return typeof value === 'object' && value !== null
|
|
146
|
+
? (value as Record<string, unknown>)
|
|
147
|
+
: undefined
|
|
148
|
+
} catch {
|
|
149
|
+
return undefined
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function read(token: string, root: string = PROJECT_ROOT): PinnedSummary | undefined {
|
|
154
|
+
const raw = stamp_file.read_stamp_text(record_path(token, root))
|
|
155
|
+
const record = raw === undefined ? undefined : parsed(raw)
|
|
156
|
+
|
|
157
|
+
if (record?.['token'] !== token) return undefined
|
|
158
|
+
|
|
159
|
+
return to_summary(record)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function clear(token: string, root: string = PROJECT_ROOT): void {
|
|
163
|
+
stamp_file.remove_stamp(record_path(token, root))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// A recorded launch's `josh test:pinned` arguments, the token last. **With no `--unpinned-ok` reason it
|
|
167
|
+
// stops at the first unpinned hunk** (joshuafolkken/kit#5153): the launcher stops on it whatever the
|
|
168
|
+
// rest say. Under a reason every hunk is probed, since a reason accepts the list as the whole answer.
|
|
169
|
+
// `origin` names the launcher on the run's ledger row (joshuafolkken/kit#5262). `is_whole` probes every
|
|
170
|
+
// hunk with no reason: the background probe that goes on past a cut check's stop (joshuafolkken/kit#5327).
|
|
171
|
+
function launch_argv(
|
|
172
|
+
token: string,
|
|
173
|
+
reason: string | undefined,
|
|
174
|
+
origin: PinnedOrigin,
|
|
175
|
+
is_whole = false,
|
|
176
|
+
): Array<string> {
|
|
177
|
+
const cut = reason === undefined && !is_whole ? [FIRST_UNPINNED_FLAG] : []
|
|
178
|
+
|
|
179
|
+
return ['test:pinned', ...cut, '--origin', origin, '--record', token]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const test_pinned_record = {
|
|
183
|
+
clear,
|
|
184
|
+
launch_argv,
|
|
185
|
+
is_text_list,
|
|
186
|
+
not_run_summary,
|
|
187
|
+
parsed,
|
|
188
|
+
partial_summary,
|
|
189
|
+
read,
|
|
190
|
+
record_path,
|
|
191
|
+
summary_of,
|
|
192
|
+
write,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export type { PinnedSummary }
|
|
196
|
+
export { test_pinned_record }
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { TypeScriptApi } from '#scripts/review/comment-ranges'
|
|
2
|
+
import type { Identifier, Node } from 'typescript'
|
|
3
|
+
import { test_pinned_functions } from './test-pinned-functions'
|
|
4
|
+
import { test_pinned_logic, type Hunk } from './test-pinned-logic'
|
|
5
|
+
|
|
6
|
+
// Which hunks `josh test:pinned` does not probe as a rewire (joshuafolkken/kit#5235): an import moved
|
|
7
|
+
// to another module under a new name, or a call renamed to it. Put back alone, such a hunk reads a name
|
|
8
|
+
// nothing in the file binds, so a test goes red exactly when it runs that line — the hunk has no
|
|
9
|
+
// behavior of its own to pin. A hunk that changes anything besides the names is still probed.
|
|
10
|
+
|
|
11
|
+
interface Names {
|
|
12
|
+
bound: Set<string>
|
|
13
|
+
used: Set<string>
|
|
14
|
+
imported: Set<string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Token {
|
|
18
|
+
kind: number
|
|
19
|
+
text: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function is_called(api: TypeScriptApi, parent: Node, node: Identifier): boolean {
|
|
23
|
+
if (!api.isCallExpression(parent) && !api.isNewExpression(parent)) return false
|
|
24
|
+
|
|
25
|
+
return parent.expression === node || parent.arguments?.includes(node) === true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A read of the name's value: the object of a member access, a callee or an argument. Any other
|
|
29
|
+
// position is left out, so a name read elsewhere never makes a hunk a rewire.
|
|
30
|
+
function is_use(api: TypeScriptApi, node: Identifier): boolean {
|
|
31
|
+
const { parent } = node
|
|
32
|
+
if (api.isPropertyAccessExpression(parent)) return parent.expression === node
|
|
33
|
+
|
|
34
|
+
return is_called(api, parent, node)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function is_import(api: TypeScriptApi, parent: Node): boolean {
|
|
38
|
+
return (
|
|
39
|
+
api.isImportSpecifier(parent) || api.isImportClause(parent) || api.isNamespaceImport(parent)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Any node's own name counts as bound — a property name too — so a binding is never missed.
|
|
44
|
+
function add_name(api: TypeScriptApi, node: Identifier, names: Names): void {
|
|
45
|
+
const { parent } = node
|
|
46
|
+
if (is_use(api, node)) names.used.add(node.text)
|
|
47
|
+
if (!('name' in parent) || parent.name !== node) return
|
|
48
|
+
names.bound.add(node.text)
|
|
49
|
+
if (is_import(api, parent)) names.imported.add(node.text)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function names_in(api: TypeScriptApi, node: Node, names: Names): void {
|
|
53
|
+
if (api.isIdentifier(node)) add_name(api, node, names)
|
|
54
|
+
api.forEachChild(node, (child) => {
|
|
55
|
+
names_in(api, child, names)
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function names_of(api: TypeScriptApi, file: string, content: string): Names {
|
|
60
|
+
const names = { bound: new Set<string>(), used: new Set<string>(), imported: new Set<string>() }
|
|
61
|
+
|
|
62
|
+
names_in(api, api.createSourceFile(file, content, api.ScriptTarget.ESNext, true), names)
|
|
63
|
+
|
|
64
|
+
return names
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The names read and bound nowhere in the file, globals left out.
|
|
68
|
+
function unbound_of(names: Names): Array<string> {
|
|
69
|
+
return [...names.used].filter(
|
|
70
|
+
(name) => !names.bound.has(name) && !Object.hasOwn(globalThis, name),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function tokens_of(api: TypeScriptApi, text: string): Array<Token> {
|
|
75
|
+
const target = api.ScriptTarget.ESNext
|
|
76
|
+
const scanner = api.createScanner(target, true, api.LanguageVariant.Standard, text)
|
|
77
|
+
const end = api.SyntaxKind.EndOfFileToken
|
|
78
|
+
const tokens: Array<Token> = []
|
|
79
|
+
|
|
80
|
+
for (let kind = scanner.scan(); kind !== end; kind = scanner.scan()) {
|
|
81
|
+
tokens.push({ kind, text: scanner.getTokenText() })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return tokens
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// A rename the revert undoes: from a name it leaves unbound to one the file imports now.
|
|
88
|
+
interface Swap {
|
|
89
|
+
from: ReadonlySet<string>
|
|
90
|
+
to: ReadonlySet<string>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function is_same_token(token: Token, other: Token | undefined, swap: Swap): boolean {
|
|
94
|
+
if (other?.kind !== token.kind) return false
|
|
95
|
+
|
|
96
|
+
return token.text === other.text || (swap.from.has(token.text) && swap.to.has(other.text))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Whether two token runs differ only where `swap` renames the old token to the new one.
|
|
100
|
+
function is_renamed_only(
|
|
101
|
+
olds: ReadonlyArray<Token>,
|
|
102
|
+
news: ReadonlyArray<Token>,
|
|
103
|
+
swap: Swap,
|
|
104
|
+
): boolean {
|
|
105
|
+
if (olds.length !== news.length) return false
|
|
106
|
+
|
|
107
|
+
return olds.every((token, index) => is_same_token(token, news[index], swap))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function is_import_only(api: TypeScriptApi, file: string, text: string): boolean {
|
|
111
|
+
const source = api.createSourceFile(file, text, api.ScriptTarget.ESNext, true)
|
|
112
|
+
|
|
113
|
+
return source.statements.every((statement) => api.isImportDeclaration(statement))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function new_text_of(content: string, hunk: Hunk): string {
|
|
117
|
+
const start = hunk.new_start - 1
|
|
118
|
+
|
|
119
|
+
return content
|
|
120
|
+
.split('\n')
|
|
121
|
+
.slice(start, start + hunk.new_count)
|
|
122
|
+
.join('\n')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The hunk's lines are import declarations alone, or the same code with only the names swapped.
|
|
126
|
+
function is_name_swap(api: TypeScriptApi, hunk: Hunk, new_text: string, swap: Swap): boolean {
|
|
127
|
+
const old_text = hunk.old_lines.join('\n')
|
|
128
|
+
const is_imports = is_import_only(api, hunk.file, old_text)
|
|
129
|
+
if (is_imports && is_import_only(api, hunk.file, new_text)) return true
|
|
130
|
+
|
|
131
|
+
return is_renamed_only(tokens_of(api, old_text), tokens_of(api, new_text), swap)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function is_rewire(api: TypeScriptApi, content: string, hunk: Hunk): boolean {
|
|
135
|
+
if (!test_pinned_functions.is_script(hunk.file)) return false
|
|
136
|
+
const after = names_of(api, hunk.file, content)
|
|
137
|
+
const before = names_of(api, hunk.file, test_pinned_logic.revert(content, hunk))
|
|
138
|
+
const unbound_now = new Set(unbound_of(after))
|
|
139
|
+
const unbound = unbound_of(before).filter((name) => !unbound_now.has(name))
|
|
140
|
+
if (unbound.length === 0) return false
|
|
141
|
+
const swap = { from: new Set(unbound), to: after.imported }
|
|
142
|
+
|
|
143
|
+
return is_name_swap(api, hunk, new_text_of(content, hunk), swap)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const test_pinned_rewire = { is_rewire }
|
|
147
|
+
|
|
148
|
+
export { test_pinned_rewire }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import type { ImportGraph } from '#scripts/lint/import-graph'
|
|
3
|
+
import { test_declared_logic } from './test-declared-logic'
|
|
4
|
+
|
|
5
|
+
// The code a failed test can run past its import closure (joshuafolkken/kit#5261): a test starts a CLI
|
|
6
|
+
// by naming its file in a string, so a file a string literal of the test or of a module it imports
|
|
7
|
+
// names is one it may start, and so is a file the strings that one runs name in turn — the command a
|
|
8
|
+
// spawned dispatcher starts by its path. Only these, with their closures, are what a `josh
|
|
9
|
+
// test:pinned` red rests on besides the test's own closure; an edit to code nothing named reaches
|
|
10
|
+
// leaves the red held. A comment starts nothing, so its file names are not read.
|
|
11
|
+
//
|
|
12
|
+
// A name is a file's whole base name in one string or template span, so a file named by another with
|
|
13
|
+
// the same base name counts too, erring toward re-probing. A name built across template spans or
|
|
14
|
+
// written without its extension is not read, so a fix to that CLI leaves its red held.
|
|
15
|
+
|
|
16
|
+
const TOKEN = /[\w.-]+/gu
|
|
17
|
+
|
|
18
|
+
interface TestSpawn {
|
|
19
|
+
started: (tests: ReadonlyArray<string>) => ReadonlySet<string>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Each non-test code file of `universe` under its base name.
|
|
23
|
+
function index_of(universe: ReadonlyArray<string>): Map<string, Array<string>> {
|
|
24
|
+
const index = new Map<string, Array<string>>()
|
|
25
|
+
|
|
26
|
+
for (const file of universe) {
|
|
27
|
+
if (test_declared_logic.is_test_file(path.relative(process.cwd(), file))) continue
|
|
28
|
+
const name = path.basename(file)
|
|
29
|
+
|
|
30
|
+
index.set(name, [...(index.get(name) ?? []), file])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return index
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function names_in(text: string, index: ReadonlyMap<string, ReadonlyArray<string>>): Array<string> {
|
|
37
|
+
return (text.match(TOKEN) ?? []).flatMap((token) => index.get(token) ?? [])
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// `read` answered once per file for the graph's lifetime, however many reds ask it.
|
|
41
|
+
function memoized(read: (file: string) => Array<string>): (file: string) => Array<string> {
|
|
42
|
+
const known = new Map<string, Array<string>>()
|
|
43
|
+
|
|
44
|
+
return function answer(file: string): Array<string> {
|
|
45
|
+
const found = known.get(file) ?? read(file)
|
|
46
|
+
|
|
47
|
+
known.set(file, found)
|
|
48
|
+
|
|
49
|
+
return found
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function create(graph: ImportGraph, universe: ReadonlyArray<string>): TestSpawn {
|
|
54
|
+
const index = index_of(universe)
|
|
55
|
+
const names_of = memoized((file) => names_in(graph.strings(file).join(' '), index))
|
|
56
|
+
// What `file` runs names: its own strings and those of every module its imports reach.
|
|
57
|
+
const named_from = memoized((file) => [
|
|
58
|
+
...new Set([file, ...graph.closure(file)].flatMap((read) => names_of(read))),
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
function started(tests: ReadonlyArray<string>): ReadonlySet<string> {
|
|
62
|
+
const found = new Set<string>()
|
|
63
|
+
const pending = [...tests]
|
|
64
|
+
|
|
65
|
+
for (let current = pending.pop(); current !== undefined; current = pending.pop()) {
|
|
66
|
+
const next = named_from(current).filter((file) => !found.has(file))
|
|
67
|
+
|
|
68
|
+
for (const file of next) found.add(file)
|
|
69
|
+
pending.push(...next)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return found
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { started }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const test_pinned_spawn = { create }
|
|
79
|
+
|
|
80
|
+
export type { TestSpawn }
|
|
81
|
+
export { test_pinned_spawn }
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { RedProbe } from './test-red-logic'
|
|
2
|
+
|
|
3
|
+
// The stages of one `josh test:pinned` probe (joshuafolkken/kit#5292): a hunk whose file reaches far
|
|
4
|
+
// more test files than import it directly runs those importers first, since a revert's red almost
|
|
5
|
+
// always lands there, and the whole reach only when they do not go red. A narrow file runs its reach
|
|
6
|
+
// once, as before.
|
|
7
|
+
|
|
8
|
+
// A reach of this many test files is wide whatever the direct importers number.
|
|
9
|
+
const WIDE_REACH = 100
|
|
10
|
+
// A reach this many times the direct importers is wide.
|
|
11
|
+
const WIDE_RATIO = 3
|
|
12
|
+
|
|
13
|
+
// What a fresh probe runs: `direct` first where the reach is wide, then `reach` unless `direct` went red.
|
|
14
|
+
interface StagePlan {
|
|
15
|
+
direct?: ReadonlyArray<string> | undefined
|
|
16
|
+
reach: ReadonlyArray<string>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// How many probes the direct importers decided, and how many escalated to the whole reach.
|
|
20
|
+
interface Staged {
|
|
21
|
+
direct: number
|
|
22
|
+
escalated: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type StageRun = (files: ReadonlyArray<string>) => Promise<RedProbe>
|
|
26
|
+
|
|
27
|
+
function none(): Staged {
|
|
28
|
+
return { direct: 0, escalated: 0 }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// No direct importer, or as many as the reach, leaves no smaller first stage to run.
|
|
32
|
+
function is_wide(reach: number, direct: number): boolean {
|
|
33
|
+
if (direct === 0 || direct >= reach) return false
|
|
34
|
+
|
|
35
|
+
return reach >= WIDE_REACH || reach >= WIDE_RATIO * direct
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// The plan for `file`, read off its `direct` importers and how many test files its imports `reach`.
|
|
39
|
+
function plan_over(file: string, direct: ReadonlyArray<string>, reach: number): StagePlan {
|
|
40
|
+
return is_wide(reach, direct.length) ? { direct, reach: [file] } : { reach: [file] }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The verdict over `plan`, each stage run through `run` and tallied into `staged`: a red from the
|
|
44
|
+
// direct importers decides, anything else is answered by the whole reach.
|
|
45
|
+
async function staged_verdict(plan: StagePlan, run: StageRun, staged: Staged): Promise<RedProbe> {
|
|
46
|
+
if (plan.direct === undefined) return await run(plan.reach)
|
|
47
|
+
const first = await run(plan.direct)
|
|
48
|
+
|
|
49
|
+
if (first.verdict === 'red') {
|
|
50
|
+
staged.direct += 1
|
|
51
|
+
|
|
52
|
+
return first
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
staged.escalated += 1
|
|
56
|
+
|
|
57
|
+
return await run(plan.reach)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The counts line's tail, empty when no probe ran in stages.
|
|
61
|
+
function counts_suffix(staged: Staged | undefined): string {
|
|
62
|
+
if (staged === undefined || staged.direct + staged.escalated === 0) return ''
|
|
63
|
+
|
|
64
|
+
return ` · direct ${String(staged.direct)} · escalated ${String(staged.escalated)}`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const test_pinned_stage = { WIDE_RATIO, WIDE_REACH, counts_suffix, none, plan_over, staged_verdict }
|
|
68
|
+
|
|
69
|
+
export type { Staged, StagePlan }
|
|
70
|
+
export { test_pinned_stage }
|