@joshuafolkken/kit 1.1784.0 → 1.2114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/reviewer-verification.md +19 -0
- package/.claude/agents/reviewer.md +19 -0
- package/.claude/settings.json +5 -6
- package/.claude/skills/dependency-update/SKILL.md +9 -10
- package/.claude/skills/workflow-commands/SKILL.md +6 -6
- package/.claude/skills/workflow-commands/background-commands.md +8 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +41 -32
- package/.claude/skills/workflow-commands/delegation.md +6 -6
- package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +7 -5
- package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +9 -4
- package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -2
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
- package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
- package/.claude/skills/workflow-commands/prerequisite.md +3 -2
- package/.claude/skills/workflow-commands/retrospective.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +7 -5
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-pnpm/action.yml +9 -16
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/auto-tag.yml +45 -0
- package/.github/workflows/ci.yml +155 -15
- package/.github/workflows/dependabot-auto-merge.yml +63 -4
- package/.github/workflows/production.yml +8 -0
- package/.github/workflows/publish.yml +59 -45
- package/.vscode/settings.json +19 -0
- package/CLAUDE.md +5 -12
- package/SECURITY.md +2 -2
- package/dist/CLAUDE.md +5 -12
- package/dist/commands/document-read-cli.js +2 -1
- package/dist/commands/document-section-main.js +2 -1
- package/dist/commands/read-set-cli.js +194 -134
- package/dist/commands/run-prep-main.js +9694 -10173
- package/dist/commands/run-step-cli.js +22138 -18285
- package/dist/hooks/audit-provision.js +23 -5
- package/dist/hooks/chunk-3GKE653K.js +6204 -0
- package/dist/hooks/chunk-45CJJOO7.js +1243 -0
- package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
- package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
- package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
- package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
- package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
- package/dist/hooks/chunk-VVNYJWEI.js +30 -0
- package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
- package/dist/hooks/codex-hook-adapter.js +8 -8
- package/dist/hooks/format-edited.js +4 -5
- package/dist/hooks/inputs.json +415 -389
- package/dist/hooks/pretool-guard.js +7 -6
- package/dist/hooks/session-lang.js +101 -258
- package/dist/hooks/stop-guard.js +1744 -2576
- package/dist/josh.js +7133 -6368
- package/dist/version/index.js +161 -1
- package/eslint/rules/comment-why-only.js +108 -0
- package/package.json +15 -8
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +14 -11
- package/prompts/collaboration-workflow/issue-citation.md +0 -4
- package/prompts/collaboration-workflow/issue-template.md +40 -21
- package/prompts/collaboration-workflow/lane-instructions.md +3 -1
- package/prompts/collaboration-workflow/measurement.md +8 -1
- package/prompts/collaboration-workflow/operating-rules.md +11 -2
- package/prompts/collaboration-workflow/output-bounds.md +6 -2
- package/prompts/collaboration-workflow/plan-comment.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/residency.md +3 -2
- package/prompts/collaboration-workflow/rule-delivery.md +13 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +3 -1
- package/prompts/review-rubric.md +8 -0
- package/prompts/review.md +31 -22
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-argv.ts +14 -16
- package/scripts/agent/agent-provider.ts +138 -0
- package/scripts/agent/agent-role-profile.ts +108 -156
- package/scripts/agent/agent-safe-text.ts +20 -5
- package/scripts/agent/api-usage-limit.ts +65 -0
- package/scripts/agent/claude-agent-argv.ts +19 -9
- package/scripts/agent/codex-agent-argv.ts +118 -22
- package/scripts/agent/usage-limit-reset.ts +179 -0
- package/scripts/auth/claude-token-output.ts +2 -2
- package/scripts/auto-ok/auto-ok-author.ts +53 -0
- package/scripts/auto-ok/auto-ok-cli.ts +20 -9
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +6 -1
- package/scripts/backlog/backlog-drive-hold.ts +3 -3
- package/scripts/backlog/backlog-drive-idle.ts +10 -0
- package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
- package/scripts/backlog/backlog-drive-launch.ts +8 -2
- package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
- package/scripts/backlog/backlog-drive-named.ts +28 -7
- package/scripts/backlog/backlog-drive-paused.ts +5 -4
- package/scripts/backlog/backlog-drive-restore.ts +11 -3
- package/scripts/backlog/backlog-drive.ts +21 -60
- package/scripts/backlog/backlog-gate-scope.ts +61 -0
- package/scripts/backlog/backlog-measure-close.ts +112 -0
- package/scripts/backlog/backlog-next.ts +39 -49
- package/scripts/backlog/backlog-occupied.ts +2 -2
- package/scripts/backlog/backlog-overlap.ts +42 -8
- package/scripts/backlog/backlog-plan-cli.ts +4 -1
- package/scripts/backlog/backlog-plan-comment.ts +81 -0
- package/scripts/backlog/backlog-plan-read.ts +19 -6
- package/scripts/backlog/backlog-pool.ts +50 -12
- package/scripts/backlog/backlog-rank.ts +60 -13
- package/scripts/backlog/backlog-stalled-detect.ts +2 -1
- package/scripts/backlog/backlog-waves.ts +17 -2
- package/scripts/backlog/backlogrun-launch.ts +7 -4
- package/scripts/cost-runtime/context-cut-payback.ts +27 -25
- package/scripts/cost-runtime/cost-cli.ts +37 -16
- package/scripts/cost-runtime/cost-pricing.ts +9 -0
- package/scripts/cost-runtime/cost-verdict.ts +4 -1
- package/scripts/delegation/delegation-policy.ts +2 -2
- package/scripts/delegation/duplicate-read-guard.ts +1 -0
- package/scripts/delegation/investigation-guard.ts +14 -2
- package/scripts/delegation/investigation-reads.ts +45 -23
- package/scripts/dependabot/dependabot-cooldown.ts +3 -2
- package/scripts/discovery/repo-party.ts +80 -20
- package/scripts/discovery/session-owner-pin.ts +51 -0
- package/scripts/document/capped-print.ts +1 -1
- package/scripts/document/command-reference-line-budget.ts +42 -0
- package/scripts/document/document-byte-budget.ts +17 -7
- package/scripts/document/document-checks.ts +20 -0
- package/scripts/document/document-ratchet-check.ts +101 -0
- package/scripts/document/document-section.ts +1 -0
- package/scripts/document/entry-read-budget.ts +7 -3
- package/scripts/document/issue-citation-budget.ts +94 -0
- package/scripts/epic/epic-cli-argv.ts +2 -2
- package/scripts/epic/epic-fetch.ts +4 -2
- package/scripts/epic/epic-graph.ts +7 -2
- package/scripts/epic/epic-issue.ts +3 -1
- package/scripts/epic/epic-rank.ts +5 -0
- package/scripts/exports/unused-members-check.ts +62 -0
- package/scripts/exports/unused-members-cli.ts +6 -51
- package/scripts/followup/followup-ci-path.ts +67 -0
- package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
- package/scripts/followup/followup-coderabbit-wait.ts +13 -10
- package/scripts/followup/followup-failed-tests.ts +160 -0
- package/scripts/followup/followup-job-needs.ts +100 -0
- package/scripts/followup/followup-job-rerun.ts +303 -0
- package/scripts/followup/followup-main-move.ts +113 -0
- package/scripts/followup/git-followup-finish.ts +43 -11
- package/scripts/followup/git-followup-pending.ts +3 -3
- package/scripts/followup/git-followup-stages.ts +41 -11
- package/scripts/followup/git-pr-followup-checks.ts +55 -16
- package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
- package/scripts/followup/git-pr-followup.ts +62 -27
- package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
- package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
- package/scripts/gate/busy-average.ts +34 -2
- package/scripts/gate/check-related-pinned.ts +43 -0
- package/scripts/gate/check-related-reuse.ts +64 -0
- package/scripts/gate/check-related-section.ts +57 -0
- package/scripts/gate/check-related-unplanned.ts +34 -0
- package/scripts/gate/check-related.ts +265 -0
- package/scripts/gate/command-memory.ts +1 -1
- package/scripts/gate/core-admission.ts +24 -6
- package/scripts/gate/core-budget-timing.ts +20 -6
- package/scripts/gate/core-budget.ts +40 -11
- package/scripts/gate/core-wait-record.ts +3 -2
- package/scripts/gate/core-wait.ts +25 -2
- package/scripts/gate/core-weights.ts +15 -7
- package/scripts/gate/gate-labels.ts +46 -0
- package/scripts/gate/gate-ledger.ts +14 -6
- package/scripts/gate/gate-plan.ts +103 -58
- package/scripts/gate/gate-report.ts +9 -7
- package/scripts/gate/gate-run-steps.ts +26 -3
- package/scripts/gate/gate-skip.ts +45 -6
- package/scripts/gate/gate-static-group.ts +60 -0
- package/scripts/gate/gate-steps.ts +2 -1
- package/scripts/gate/gate-tree.ts +4 -2
- package/scripts/gate/hook-gate-reuse.ts +19 -2
- package/scripts/gate/metrics-totals.ts +59 -0
- package/scripts/gate/scoped-green.ts +68 -15
- package/scripts/gate/since-green.ts +14 -6
- package/scripts/gate/structural-checks.ts +96 -0
- package/scripts/gate/verification-gate.ts +51 -16
- package/scripts/gate/verification-inputs.ts +21 -6
- package/scripts/gate/verification-tail.ts +52 -0
- package/scripts/gh/gh-author-association.ts +23 -2
- package/scripts/gh/git-ai-review-scan.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +12 -3
- package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
- package/scripts/gh/git-gh-exec.ts +32 -2
- package/scripts/gh/git-gh-issue-read.ts +4 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +8 -2
- package/scripts/gh/git-gh-issue.ts +12 -5
- package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
- package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
- package/scripts/gh/git-gh-pr-read.ts +74 -13
- package/scripts/gh/git-gh-pr-rest.ts +3 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
- package/scripts/gh/git-gh-pr-too-large.ts +20 -0
- package/scripts/gh/git-gh-pr.ts +21 -7
- package/scripts/gh/git-gh-retry.ts +141 -0
- package/scripts/gh/git-pr-ai-review.ts +15 -6
- package/scripts/gh/git-pr-checks-eval.ts +3 -0
- package/scripts/gh/git-pr-checks.ts +5 -5
- package/scripts/git/base-tree.ts +30 -0
- package/scripts/git/change-base.ts +4 -10
- package/scripts/git/changed-paths-sync.ts +63 -0
- package/scripts/git/changed-paths.ts +16 -1
- package/scripts/git/git-common-directory.ts +11 -1
- package/scripts/git/git-diff-reads.ts +66 -20
- package/scripts/git/git-error.ts +9 -2
- package/scripts/git/git-schemas.ts +13 -0
- package/scripts/git/git-worktree.ts +13 -0
- package/scripts/git/gone-branch.ts +4 -1
- package/scripts/git/main-merge-budget-conflict.ts +89 -0
- package/scripts/git/main-merge-guard.ts +55 -18
- package/scripts/git/main-merge-regenerate.ts +59 -25
- package/scripts/git/main-merge.ts +53 -5
- package/scripts/git/no-base-checkout.ts +127 -0
- package/scripts/git/stash/git-stash.ts +11 -2
- package/scripts/git/stash/stash-push-cli.ts +10 -0
- package/scripts/hooks/batch-guard.ts +40 -21
- package/scripts/hooks/edited-cspell.ts +37 -11
- package/scripts/hooks/format-edited-file.ts +22 -2
- package/scripts/hooks/lane-plan-guard.ts +50 -0
- package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
- package/scripts/hooks/pre-commit-lint.ts +14 -1
- package/scripts/hooks/pre-push-related-switch.ts +10 -0
- package/scripts/hooks/pre-push-unit.ts +76 -10
- package/scripts/hooks/pretool-guard.ts +2 -0
- package/scripts/hooks/reserved-run.ts +14 -1
- package/scripts/hooks/run-hook.sh +23 -8
- package/scripts/hooks/step-zero-notice.ts +6 -1
- package/scripts/hooks/stop-actions.ts +3 -15
- package/scripts/hooks/stop-guard.ts +7 -6
- package/scripts/init/distributed-paths.ts +1 -0
- package/scripts/init/hook-command-rewrite.ts +5 -1
- package/scripts/init/hook-launch.ts +4 -2
- package/scripts/issue/aggregation-split.ts +52 -0
- package/scripts/issue/defect-origin-line.ts +19 -1
- package/scripts/issue/defect-rate-cli.ts +5 -102
- package/scripts/issue/defect-report.ts +4 -4
- package/scripts/issue/effect-audit-cli.ts +178 -0
- package/scripts/issue/effect-audit.ts +185 -0
- package/scripts/issue/expected-effect-lint.ts +64 -9
- package/scripts/issue/git-next-issues.ts +5 -0
- package/scripts/issue/issue-body-or-comment.ts +38 -4
- package/scripts/issue/issue-cite-cli.ts +1 -3
- package/scripts/issue/issue-classification.ts +1 -0
- package/scripts/issue/issue-closing-pr.ts +8 -1
- package/scripts/issue/issue-evidence-exit.ts +4 -2
- package/scripts/issue/issue-file-cap.ts +58 -0
- package/scripts/issue/issue-file-cli.ts +111 -38
- package/scripts/issue/issue-file-deferral.ts +52 -0
- package/scripts/issue/issue-file-fold.ts +29 -10
- package/scripts/issue/issue-file-interrupt.ts +85 -0
- package/scripts/issue/issue-file-lane-hint.ts +30 -0
- package/scripts/issue/issue-file.ts +43 -6
- package/scripts/issue/issue-labels.ts +19 -2
- package/scripts/issue/issue-lint-cli.ts +27 -2
- package/scripts/issue/issue-measured-cli.ts +13 -35
- package/scripts/issue/issue-measured-close.ts +58 -0
- package/scripts/issue/issue-not-before-label.ts +2 -2
- package/scripts/issue/issue-not-before.ts +4 -1
- package/scripts/issue/issue-priority-cli.ts +60 -0
- package/scripts/issue/issue-priority-raise.ts +55 -0
- package/scripts/issue/issue-priority.ts +174 -43
- package/scripts/issue/issue-rank.ts +21 -4
- package/scripts/issue/issue-read-cli.ts +32 -6
- package/scripts/issue/issue-read.ts +37 -12
- package/scripts/issue/issue-sample-gate.ts +95 -74
- package/scripts/issue/issue-sample-prime.ts +127 -0
- package/scripts/issue/issue-sample-required.ts +57 -0
- package/scripts/issue/issue-sample-source.ts +12 -11
- package/scripts/issue/issue-search.ts +124 -0
- package/scripts/issue/issue-state.ts +13 -1
- package/scripts/issue/issue-template-section.ts +43 -0
- package/scripts/issue/issue-watch-diff.ts +31 -1
- package/scripts/issue/issue-watch-hook.ts +11 -2
- package/scripts/issue/issue-watch.ts +55 -12
- package/scripts/issue/markdown-section.ts +1 -0
- package/scripts/issue/measure-declaration.ts +121 -0
- package/scripts/issue/measure-rerun-cli.ts +7 -70
- package/scripts/issue/plan-consumers-section.ts +22 -1
- package/scripts/issue/reproduction-measure.ts +3 -1
- package/scripts/issue/size-estimate.ts +90 -0
- package/scripts/issue/trusted-measure.ts +84 -0
- package/scripts/josh/command-usage.ts +3 -2
- package/scripts/josh/durable-state-migrate.ts +105 -0
- package/scripts/josh/durable-state.ts +112 -0
- package/scripts/josh/josh-aliases.ts +95 -0
- package/scripts/josh/josh-command-map.ts +1 -82
- package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
- package/scripts/josh/josh-commands-ai.ts +5 -5
- package/scripts/josh/josh-commands-development.ts +59 -58
- package/scripts/josh/josh-commands-issue-write.ts +23 -4
- package/scripts/josh/josh-commands-issue.ts +32 -0
- package/scripts/josh/josh-commands-lane.ts +87 -4
- package/scripts/josh/josh-commands-lint.ts +16 -3
- package/scripts/josh/josh-commands-maintenance.ts +15 -0
- package/scripts/josh/josh-commands-review.ts +41 -6
- package/scripts/josh/josh-commands-ship.ts +56 -8
- package/scripts/josh/josh-commands-test-check.ts +80 -0
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/josh-environment-file.ts +43 -3
- package/scripts/josh/josh-in-process.ts +2 -2
- package/scripts/josh/josh-logic.ts +5 -3
- package/scripts/josh/josh-run.ts +19 -1
- package/scripts/josh/run-record-path.ts +15 -0
- package/scripts/josh/stamp-file.ts +11 -2
- package/scripts/lane/lane-adopt-cli.ts +98 -0
- package/scripts/lane/lane-adopt.ts +208 -0
- package/scripts/lane/lane-await.ts +5 -8
- package/scripts/lane/lane-baseline.ts +123 -0
- package/scripts/lane/lane-breakdown-cli.ts +64 -0
- package/scripts/lane/lane-breakdown-implement.ts +170 -0
- package/scripts/lane/lane-breakdown-laps.ts +107 -0
- package/scripts/lane/lane-breakdown-pinned.ts +76 -0
- package/scripts/lane/lane-breakdown-text.ts +145 -0
- package/scripts/lane/lane-breakdown.ts +239 -0
- package/scripts/lane/lane-check-stats.ts +29 -8
- package/scripts/lane/lane-child-invocation.ts +9 -2
- package/scripts/lane/lane-child-mark-keys.ts +12 -0
- package/scripts/lane/lane-child-marker.ts +20 -1
- package/scripts/lane/lane-ci-path-stats.ts +73 -0
- package/scripts/lane/lane-claim-stats.ts +84 -0
- package/scripts/lane/lane-close.ts +8 -3
- package/scripts/lane/lane-dispatch-log.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +2 -2
- package/scripts/lane/lane-gap-stats.ts +7 -6
- package/scripts/lane/lane-guard-policy.ts +7 -1
- package/scripts/lane/lane-implement-stats.ts +207 -0
- package/scripts/lane/lane-implement-subagents.ts +90 -0
- package/scripts/lane/lane-investigation-switch.ts +16 -0
- package/scripts/lane/lane-ledger-kind.ts +31 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
- package/scripts/lane/lane-ledger-schema.ts +119 -36
- package/scripts/lane/lane-ledger-write.ts +257 -0
- package/scripts/lane/lane-ledger.ts +31 -177
- package/scripts/lane/lane-load.ts +1 -1
- package/scripts/lane/lane-measure-cli.ts +176 -59
- package/scripts/lane/lane-net-read.ts +44 -0
- package/scripts/lane/lane-net-time.ts +212 -0
- package/scripts/lane/lane-open.ts +16 -7
- package/scripts/lane/lane-output.ts +2 -4
- package/scripts/lane/lane-overrun-prompt.ts +147 -0
- package/scripts/lane/lane-overrun.ts +274 -0
- package/scripts/lane/lane-paths.ts +24 -1
- package/scripts/lane/lane-pause-cli.ts +4 -1
- package/scripts/lane/lane-paused-spans.ts +97 -0
- package/scripts/lane/lane-plan-check.ts +207 -0
- package/scripts/lane/lane-plan-cli.ts +41 -0
- package/scripts/lane/lane-plan.ts +144 -0
- package/scripts/lane/lane-registry.ts +14 -1
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-round-one-stats.ts +51 -0
- package/scripts/lane/lane-sampler.ts +23 -4
- package/scripts/lane/lane-session-action.ts +93 -18
- package/scripts/lane/lane-session-broker.ts +49 -21
- package/scripts/lane/lane-session-reread-main.ts +14 -0
- package/scripts/lane/lane-session-reread.ts +51 -0
- package/scripts/lane/lane-ship-lap-stats.ts +74 -0
- package/scripts/lane/lane-sleep.ts +58 -0
- package/scripts/lane/lane-spent.ts +1 -1
- package/scripts/lane/lane-split-stats.ts +28 -0
- package/scripts/lane/lane-stage-stats.ts +13 -1
- package/scripts/lane/lane-state.ts +41 -9
- package/scripts/lane/lane-stats-tables.ts +58 -0
- package/scripts/lane/lane-stats.ts +21 -5
- package/scripts/lane/lane-stop-stats.ts +51 -19
- package/scripts/lane/lane-tail-stats.ts +32 -0
- package/scripts/lane/lane-transcript-calls.ts +67 -10
- package/scripts/lane/lane-transcript-stats.ts +44 -12
- package/scripts/lane/lane-value-cell.ts +72 -0
- package/scripts/lane/lane-value.ts +118 -0
- package/scripts/lane/lane-withdraw-closed.ts +124 -0
- package/scripts/lane/lane-withdraw.ts +108 -31
- package/scripts/lane/lane-yield.ts +85 -0
- package/scripts/lane/openai-lane-ship.ts +12 -11
- package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
- package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
- package/scripts/lane/openai-lane-supervisor.ts +5 -4
- package/scripts/lib/poll.ts +38 -9
- package/scripts/lib/settled-together.ts +10 -0
- package/scripts/lib/shell-quote.ts +21 -0
- package/scripts/lib/token-shape.ts +111 -0
- package/scripts/lint/eslint-daemon-sources.ts +79 -0
- package/scripts/lint/eslint-daemon.ts +45 -6
- package/scripts/lint/eslint-warm-cli.ts +7 -0
- package/scripts/lint/eslint-warm.ts +66 -0
- package/scripts/lint/import-graph.ts +141 -11
- package/scripts/lint/lint-gate-scope.ts +2 -2
- package/scripts/lint/lint-parallel.ts +54 -6
- package/scripts/lint/lint-related.ts +154 -33
- package/scripts/lint/rename-cli.ts +179 -0
- package/scripts/lint/rename-identifier.ts +192 -0
- package/scripts/lint/type-only-imports.ts +58 -0
- package/scripts/notify/telegram-notify.ts +12 -3
- package/scripts/observations/observation-record.ts +42 -16
- package/scripts/observations/observations-flush.ts +32 -9
- package/scripts/overrides/overrides-check.ts +9 -1
- package/scripts/release/release-approval-prune.ts +7 -1
- package/scripts/repo/required-checks-logic.ts +4 -1
- package/scripts/repo/ruleset-check.ts +4 -3
- package/scripts/report/markdown-shape.ts +28 -0
- package/scripts/report/report-format-reference.ts +15 -1
- package/scripts/report/report-labels.ts +12 -1
- package/scripts/report/report-lint-child-summary.ts +94 -0
- package/scripts/report/report-lint-cli.ts +26 -7
- package/scripts/report/report-lint.ts +2 -20
- package/scripts/review/prose-stale-cli.ts +88 -0
- package/scripts/review/prose-stale-scan.ts +152 -0
- package/scripts/review/review-attest.ts +73 -23
- package/scripts/review/review-brief-cli.ts +68 -19
- package/scripts/review/review-brief-paths.ts +22 -0
- package/scripts/review/review-brief.ts +14 -7
- package/scripts/review/review-comment-carry.ts +47 -0
- package/scripts/review/review-comment-round.ts +85 -22
- package/scripts/review/review-finding-ledger.ts +109 -7
- package/scripts/review/review-finding-text.ts +43 -0
- package/scripts/review/review-interaction.ts +11 -0
- package/scripts/review/review-lane-handover.ts +68 -0
- package/scripts/review/review-lint-cli.ts +57 -0
- package/scripts/review/review-lint-findings.ts +92 -0
- package/scripts/review/review-lint-sections.ts +80 -0
- package/scripts/review/review-lint.ts +221 -0
- package/scripts/review/review-prior-findings.ts +102 -0
- package/scripts/review/review-readers-scan.ts +4 -2
- package/scripts/review/review-record-cli.ts +177 -54
- package/scripts/review/review-record-code-round.ts +55 -0
- package/scripts/review/review-record-comment.ts +60 -0
- package/scripts/review/review-record-interaction.ts +35 -0
- package/scripts/review/review-record-pinned.ts +108 -0
- package/scripts/review/review-record.ts +25 -1
- package/scripts/review/review-reviewed-tree.ts +307 -0
- package/scripts/review/review-round-one-closure.ts +62 -0
- package/scripts/review/review-round-reuse.ts +106 -0
- package/scripts/review/review-round2-cli.ts +91 -29
- package/scripts/review/review-round2-skip-comment.ts +55 -0
- package/scripts/review/review-round2.ts +24 -8
- package/scripts/review/review-stamps.ts +7 -3
- package/scripts/review/review-stop-log.ts +20 -6
- package/scripts/review/review-stops-cli.ts +2 -1
- package/scripts/review/review-submit-cli.ts +101 -0
- package/scripts/rules/agent-model.ts +1 -1
- package/scripts/rules/bash-triggers.ts +3 -3
- package/scripts/rules/concise-reply.ts +88 -0
- package/scripts/rules/credential-body.ts +77 -0
- package/scripts/rules/decision-oracle.ts +1 -1
- package/scripts/rules/delivered-rules.ts +36 -17
- package/scripts/rules/destructive-command.ts +3 -11
- package/scripts/rules/direct-issue-comment.ts +34 -12
- package/scripts/rules/direct-issue-read.ts +191 -0
- package/scripts/rules/direct-pr-create.ts +4 -24
- package/scripts/rules/early-heartbeat.ts +2 -0
- package/scripts/rules/edit-tool.ts +27 -0
- package/scripts/rules/file-edit-rows.ts +17 -0
- package/scripts/rules/file-rewrite.ts +146 -0
- package/scripts/rules/firing-point.ts +3 -2
- package/scripts/rules/gh-api.ts +128 -7
- package/scripts/rules/gh-repo-selector.ts +84 -0
- package/scripts/rules/git-force.ts +2 -2
- package/scripts/rules/guard-remedy.ts +14 -5
- package/scripts/rules/implementation-cut.ts +61 -28
- package/scripts/rules/investigator-foreground.ts +231 -0
- package/scripts/rules/issue-citation.ts +15 -60
- package/scripts/rules/issue-comments.ts +9 -4
- package/scripts/rules/josh-git-bare.ts +1 -1
- package/scripts/rules/lane-background.ts +41 -13
- package/scripts/rules/lane-direct-tool.ts +107 -9
- package/scripts/rules/lane-interactive-ask.ts +4 -1
- package/scripts/rules/lane-park.ts +4 -2
- package/scripts/rules/lane-spilled-read.ts +90 -0
- package/scripts/rules/lane-split-park.ts +1 -1
- package/scripts/rules/lane-switch-main.ts +4 -4
- package/scripts/rules/last-prompt.ts +20 -47
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/permission-guards.ts +5 -1
- package/scripts/rules/piped-verification-shapes.ts +110 -0
- package/scripts/rules/piped-verification.ts +67 -17
- package/scripts/rules/poll-loop.ts +72 -9
- package/scripts/rules/pr-merge.ts +5 -5
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +88 -24
- package/scripts/rules/reply-language.ts +3 -17
- package/scripts/rules/reply-prose.ts +34 -0
- package/scripts/rules/repo-admin.ts +132 -0
- package/scripts/rules/reviewer-foreground.ts +88 -0
- package/scripts/rules/round-one-check-related.ts +120 -0
- package/scripts/rules/rule-body-guard.ts +5 -15
- package/scripts/rules/rule-value-cache.ts +53 -5
- package/scripts/rules/rule-value-candidates.ts +84 -0
- package/scripts/rules/rule-value-cli.ts +59 -11
- package/scripts/rules/rule-value.ts +17 -4
- package/scripts/rules/run-tail-rule.ts +1 -1
- package/scripts/rules/sed-in-place.ts +196 -0
- package/scripts/rules/shell-segments.ts +10 -2
- package/scripts/rules/stop-rule-rows.ts +1 -13
- package/scripts/rules/stop-rules.ts +33 -58
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +110 -23
- package/scripts/rules/timed-rerun.ts +1 -1
- package/scripts/rules/turn-filings.ts +90 -0
- package/scripts/rules/worktree-guard.ts +22 -7
- package/scripts/run/board/run-board-ending-reason.ts +6 -2
- package/scripts/run/board/run-board-fit.ts +24 -1
- package/scripts/run/board/run-board-found.ts +134 -0
- package/scripts/run/board/run-board-header.ts +8 -0
- package/scripts/run/board/run-board-labels.ts +11 -1
- package/scripts/run/board/run-board-lane-time.ts +1 -0
- package/scripts/run/board/run-board-link.ts +15 -2
- package/scripts/run/board/run-board-notes.ts +7 -1
- package/scripts/run/board/run-board-operation.ts +4 -2
- package/scripts/run/board/run-board-phase.ts +2 -5
- package/scripts/run/board/run-board-plan.ts +1 -1
- package/scripts/run/board/run-board-read.ts +46 -1
- package/scripts/run/board/run-board-reason.ts +20 -8
- package/scripts/run/board/run-board-render-checks.ts +3 -2
- package/scripts/run/board/run-board-render-drawn.ts +11 -0
- package/scripts/run/board/run-board-render-found.ts +157 -0
- package/scripts/run/board/run-board-render-lead.ts +9 -0
- package/scripts/run/board/run-board-render-legend.ts +19 -0
- package/scripts/run/board/run-board-render-tail.ts +1 -1
- package/scripts/run/board/run-board-render.ts +82 -18
- package/scripts/run/board/run-board-spin.ts +1 -3
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +39 -10
- package/scripts/run/carry/run-carry-ended.ts +3 -2
- package/scripts/run/carry/run-carry.ts +3 -2
- package/scripts/run/cut/run-cut-cli.ts +33 -133
- package/scripts/run/cut/run-cut-digest.ts +152 -0
- package/scripts/run/cut/run-cut-handoff.ts +52 -8
- package/scripts/run/cut/run-cut-report.ts +24 -3
- package/scripts/run/cut/run-cut-resume.ts +129 -0
- package/scripts/run/cut/run-cut-review.ts +105 -0
- package/scripts/run/cut/run-cut.ts +18 -7
- package/scripts/run/detached-launch.ts +1 -1
- package/scripts/run/entry/run-entry-cli.ts +88 -19
- package/scripts/run/entry/run-entry-spill.ts +83 -0
- package/scripts/run/entry/run-entry-stop.ts +5 -1
- package/scripts/run/entry/run-entry-trust.ts +91 -0
- package/scripts/run/event/run-event-baseline.ts +56 -0
- package/scripts/run/event/run-event-filed.ts +9 -3
- package/scripts/run/event/run-event-overrun.ts +197 -0
- package/scripts/run/event/run-event-render.ts +4 -0
- package/scripts/run/event/run-event-stream.ts +25 -3
- package/scripts/run/hold/run-stop-notify.ts +3 -8
- package/scripts/run/merge/run-merge-cap.ts +57 -0
- package/scripts/run/merge/run-merge-cli.ts +66 -23
- package/scripts/run/merge/run-merge-outcome.ts +2 -0
- package/scripts/run/merge/run-merge-ship.ts +13 -3
- package/scripts/run/merge/run-merge-signals.ts +40 -23
- package/scripts/run/merge/run-merge-steps.ts +56 -36
- package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
- package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
- package/scripts/run/merge/run-merge.ts +41 -14
- package/scripts/run/review/run-review-cli.ts +306 -20
- package/scripts/run/review/run-review-early-push.ts +121 -0
- package/scripts/run/review/run-review-join-read.ts +94 -0
- package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
- package/scripts/run/review/run-review-pinned-join.ts +216 -0
- package/scripts/run/review/run-review-pinned-steps.ts +383 -0
- package/scripts/run/review/run-review-pinned-tree.ts +37 -0
- package/scripts/run/review/run-review-pinned.ts +53 -0
- package/scripts/run/review/run-review-steps.ts +70 -34
- package/scripts/run/review/run-review.ts +26 -6
- package/scripts/run/run-parent-cut-hook.ts +3 -4
- package/scripts/run/run-prep.ts +1 -0
- package/scripts/run/run-stage-read.ts +2 -6
- package/scripts/run/run-step-cli.ts +17 -5
- package/scripts/run/run-step-lane-plan.ts +27 -0
- package/scripts/run/run-step-section.ts +26 -7
- package/scripts/run/run-step.ts +5 -2
- package/scripts/run/ship/run-ship-args.ts +19 -0
- package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
- package/scripts/run/ship/run-ship-cli.ts +131 -39
- package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
- package/scripts/run/ship/run-ship-detach.ts +10 -4
- package/scripts/run/ship/run-ship-handover.ts +106 -0
- package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
- package/scripts/run/ship/run-ship-lap.ts +63 -0
- package/scripts/run/ship/run-ship-next.ts +27 -26
- package/scripts/run/ship/run-ship-overlap.ts +8 -3
- package/scripts/run/ship/run-ship-place.ts +9 -2
- package/scripts/run/ship/run-ship-planned.ts +92 -0
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
- package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
- package/scripts/run/ship/run-ship-preflight.ts +16 -4
- package/scripts/run/ship/run-ship-probe.ts +23 -9
- package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
- package/scripts/run/ship/run-ship-relaunch.ts +51 -0
- package/scripts/run/ship/run-ship-return.ts +103 -29
- package/scripts/run/ship/run-ship-review-reason.ts +24 -0
- package/scripts/run/ship/run-ship-review-steps.ts +230 -59
- package/scripts/run/ship/run-ship-review.ts +171 -51
- package/scripts/run/ship/run-ship-reviewer.ts +138 -42
- package/scripts/run/ship/run-ship-scoped.ts +39 -10
- package/scripts/run/ship/run-ship-stage.ts +49 -13
- package/scripts/run/ship/run-ship-steps.ts +24 -4
- package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
- package/scripts/run/ship/run-ship-stop-context.ts +50 -13
- package/scripts/run/ship/run-ship-sync.ts +171 -30
- package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
- package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
- package/scripts/run/ship/run-ship-waiter.ts +114 -0
- package/scripts/run/ship/run-ship.ts +16 -2
- package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
- package/scripts/run/wake/run-wake-cli.ts +6 -113
- package/scripts/run/wake/run-wake-code.ts +53 -11
- package/scripts/run/wake/run-wake-driver.ts +5 -3
- package/scripts/run/wake/run-wake-finish.ts +137 -0
- package/scripts/run/wake/run-wake-loop.ts +5 -3
- package/scripts/run/wake/run-wake-ports.ts +11 -3
- package/scripts/run/wake/run-wake-session.ts +2 -0
- package/scripts/run/wake/run-wake.ts +3 -2
- package/scripts/safe-chain/ci-installer-pin.ts +49 -8
- package/scripts/safe-chain/project-config.ts +3 -2
- package/scripts/security/secretlint-scan.ts +42 -9
- package/scripts/security/security-audit-provision-main.ts +9 -1
- package/scripts/security/security-audit-provision.ts +1 -1
- package/scripts/security/security-audit.ts +21 -1
- package/scripts/security/sonar-file.ts +10 -5
- package/scripts/split/split-assess.ts +12 -3
- package/scripts/split/split-early-notice.ts +116 -0
- package/scripts/split/split-measure.ts +20 -2
- package/scripts/split/split-resolution.ts +107 -0
- package/scripts/test/direct-importers.ts +185 -0
- package/scripts/test/pure-files-entries.ts +83 -0
- package/scripts/test/related-heavy-tests.ts +81 -0
- package/scripts/test/test-no-base.ts +88 -0
- package/scripts/test/test-pinned-cache-logic.ts +363 -0
- package/scripts/test/test-pinned-cache.ts +337 -0
- package/scripts/test/test-pinned-emit.ts +49 -0
- package/scripts/test/test-pinned-functions.ts +116 -0
- package/scripts/test/test-pinned-ledger.ts +80 -0
- package/scripts/test/test-pinned-logic.ts +208 -20
- package/scripts/test/test-pinned-main.ts +84 -18
- package/scripts/test/test-pinned-pool.ts +73 -0
- package/scripts/test/test-pinned-probe.ts +254 -0
- package/scripts/test/test-pinned-prune-logic.ts +118 -0
- package/scripts/test/test-pinned-reach.ts +194 -0
- package/scripts/test/test-pinned-record.ts +196 -0
- package/scripts/test/test-pinned-rewire.ts +148 -0
- package/scripts/test/test-pinned-spawn.ts +81 -0
- package/scripts/test/test-pinned-stage.ts +70 -0
- package/scripts/test/test-pinned.ts +258 -47
- package/scripts/test/test-reach-cli.ts +61 -0
- package/scripts/test/test-read-map.ts +35 -11
- package/scripts/test/test-read-recording.ts +17 -5
- package/scripts/test/test-read-trace.ts +26 -8
- package/scripts/test/test-red-logic.ts +46 -7
- package/scripts/test/test-red.ts +34 -14
- package/scripts/test/test-related-claim.ts +37 -0
- package/scripts/test/test-related-main.ts +4 -1
- package/scripts/test/test-related-moved.ts +91 -0
- package/scripts/test/test-related-record.ts +43 -23
- package/scripts/test/test-related-scope.ts +99 -34
- package/scripts/test/test-related-synced.ts +135 -0
- package/scripts/test/test-related-touched.ts +84 -0
- package/scripts/test/test-related.ts +167 -83
- package/scripts/test/test-unit-guard.ts +28 -5
- package/scripts/test/unit-durations.ts +41 -0
- package/scripts/test/unit-worker-share.ts +5 -4
- package/scripts/test/vitest-cache-directory.ts +19 -0
- package/scripts/test/vitest-cli-excludes.ts +14 -0
- package/scripts/test/vitest-failure-summary.ts +111 -0
- package/scripts/test/whole-suite-files.ts +12 -1
- package/scripts/time-runtime/time-batch-guard.ts +42 -4
- package/scripts/time-runtime/time-bundle-call.ts +1 -1
- package/scripts/time-runtime/time-bundles.ts +36 -5
- package/scripts/time-runtime/time-markers.ts +2 -2
- package/scripts/time-runtime/time-search-call.ts +38 -0
- package/scripts/time-runtime/time-shell.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +17 -5
- package/scripts/version/development-engines-verdict.ts +124 -0
- package/scripts/version/latest-corepack.ts +2 -0
- package/templates/gitignore +2 -0
- package/templates/workflows/ci.yml +41 -0
- package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
- package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
- package/dist/hooks/chunk-TUIFS5E7.js +0 -72
- package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
- package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
- package/scripts/rules/filing-cap.ts +0 -139
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { issue_bug_label } from './issue-bug-label'
|
|
2
2
|
import { markdown_section } from './markdown-section'
|
|
3
|
+
import { reproduction_measure } from './reproduction-measure'
|
|
3
4
|
|
|
4
5
|
// The origin line a bug body declares under its background heading: the merged pull request that
|
|
5
6
|
// introduced the defect, or "unknown" with the reason it could not be traced. `defect:report` groups
|
|
@@ -12,6 +13,8 @@ const MISSING_ORIGIN =
|
|
|
12
13
|
'missing origin line: a `- 種別: 不具合` body declares `- 原因: #<PR>` or `- 原因: 不明(<reason>)` under ## 背景 (`pnpm josh defect:origin <N>` lists candidates)'
|
|
13
14
|
const MALFORMED_ORIGIN =
|
|
14
15
|
'malformed origin line: write `- 原因: #<PR>` or `- 原因: 不明(<reason>)` with a non-empty reason'
|
|
16
|
+
const MISSING_REPRODUCTION =
|
|
17
|
+
'missing reproduction: a `- 原因: 不明(<reason>)` bug carries ## 再現 with a backticked command and its actual output in a fenced block'
|
|
15
18
|
|
|
16
19
|
type DefectOrigin = { kind: 'pr'; pr: number } | { kind: 'unknown'; reason: string }
|
|
17
20
|
|
|
@@ -54,7 +57,22 @@ function problem(body: string): string | undefined {
|
|
|
54
57
|
return lines.every((line) => parse_line(line) !== undefined) ? undefined : MALFORMED_ORIGIN
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
// An untraced bug owes the lane a way to see it: without a reproduction the run starts by
|
|
61
|
+
// investigating. A traced one (a pull request origin) already names where to look, so it owes none.
|
|
62
|
+
function reproduction_problem(body: string): string | undefined {
|
|
63
|
+
if (!issue_bug_label.is_bug_fix(body) || parse(body)?.kind !== 'unknown') return undefined
|
|
64
|
+
|
|
65
|
+
return reproduction_measure.has_command_output(body) ? undefined : MISSING_REPRODUCTION
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const defect_origin_line = {
|
|
69
|
+
MALFORMED_ORIGIN,
|
|
70
|
+
MISSING_ORIGIN,
|
|
71
|
+
MISSING_REPRODUCTION,
|
|
72
|
+
parse,
|
|
73
|
+
problem,
|
|
74
|
+
reproduction_problem,
|
|
75
|
+
}
|
|
58
76
|
|
|
59
77
|
export type { DefectOrigin }
|
|
60
78
|
export { defect_origin_line }
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
2
|
-
import { issue_label_schema } from '#scripts/git/git-schemas'
|
|
3
|
-
import { parse_json } from '#scripts/git/parse-json-array'
|
|
4
1
|
import { shared_cache, type CacheSlot } from '#scripts/josh/shared-cache'
|
|
5
|
-
import { bounded_pool } from '#scripts/lib/bounded-pool'
|
|
6
2
|
import { z } from 'zod'
|
|
7
|
-
import { defect_rate, type DefectRate
|
|
3
|
+
import { defect_rate, type DefectRate } from './defect-rate'
|
|
4
|
+
import { issue_search } from './issue-search'
|
|
8
5
|
|
|
9
|
-
const PER_PAGE = '100'
|
|
10
|
-
const FIRST_PAGE = 1
|
|
11
|
-
const SECOND_PAGE = 2
|
|
12
|
-
// The search API serves at most this many results, so a window past it holds no further page.
|
|
13
|
-
const SEARCH_RESULT_CEILING = 1000
|
|
14
|
-
// The search API allows 30 requests a minute; both searches together stay at the ~10 requests the
|
|
15
|
-
// sequential walk made, so this bounds only how many are in flight at once.
|
|
16
|
-
const PAGE_CONCURRENCY = 4
|
|
17
6
|
const MS_PER_MINUTE = 60_000
|
|
18
7
|
const CACHE_TTL_MINUTES = 15
|
|
19
8
|
// A 14-day rate does not move within a quarter of an hour, and every `backlog:next` / `backlog:plan`
|
|
@@ -32,100 +21,14 @@ const defect_rate_schema = z.object({
|
|
|
32
21
|
is_capped: z.boolean(),
|
|
33
22
|
})
|
|
34
23
|
|
|
35
|
-
const search_item_schema = z.object({
|
|
36
|
-
body: z.string().nullish(),
|
|
37
|
-
labels: z.array(issue_label_schema),
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
// One page of `search/issues`.
|
|
41
|
-
const search_page_schema = z.object({
|
|
42
|
-
total_count: z.number(),
|
|
43
|
-
incomplete_results: z.boolean(),
|
|
44
|
-
items: z.array(search_item_schema),
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
type SearchPage = z.infer<typeof search_page_schema>
|
|
48
|
-
|
|
49
|
-
interface SearchResult {
|
|
50
|
-
issues: ReadonlyArray<RateIssue>
|
|
51
|
-
is_capped: boolean
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function search_path(query: string, page: number = FIRST_PAGE): string {
|
|
55
|
-
const parameters = new URLSearchParams({ q: query, per_page: PER_PAGE })
|
|
56
|
-
|
|
57
|
-
if (page !== FIRST_PAGE) parameters.set('page', String(page))
|
|
58
|
-
|
|
59
|
-
return `search/issues?${parameters.toString()}`
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// How many pages the search holds, read off the first page's own count. Bounded by the API's
|
|
63
|
-
// ceiling, past which GitHub serves no page at all — so this asks for exactly the pages `gh api
|
|
64
|
-
// --paginate` would walk to.
|
|
65
|
-
function page_count(first: SearchPage): number {
|
|
66
|
-
return Math.ceil(Math.min(first.total_count, SEARCH_RESULT_CEILING) / Number(PER_PAGE))
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function read_page(query: string, page: number): Promise<SearchPage | undefined> {
|
|
70
|
-
const raw = await git_gh_exec.exec_gh_api({ path: search_path(query, page) })
|
|
71
|
-
|
|
72
|
-
return parse_json.parse_json_object_or_undefined(raw, search_page_schema)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Every page of one search. Not `gh api --paginate`: it follows each page's `next` link one page
|
|
76
|
-
// after another, at about 1.5 s a page, on every `backlog:plan`. The first page names the total, so
|
|
77
|
-
// the rest are fetched together; any page that does not read fails the whole search.
|
|
78
|
-
async function read_pages(query: string): Promise<ReadonlyArray<SearchPage> | undefined> {
|
|
79
|
-
const first = await read_page(query, FIRST_PAGE)
|
|
80
|
-
|
|
81
|
-
if (first === undefined) return undefined
|
|
82
|
-
|
|
83
|
-
const rest_numbers = Array.from(
|
|
84
|
-
{ length: Math.max(page_count(first) - FIRST_PAGE, 0) },
|
|
85
|
-
(_value, index) => index + SECOND_PAGE,
|
|
86
|
-
)
|
|
87
|
-
const rest = await bounded_pool.bounded_map(
|
|
88
|
-
rest_numbers,
|
|
89
|
-
PAGE_CONCURRENCY,
|
|
90
|
-
async (page) => await read_page(query, page),
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
return rest.every((page) => page !== undefined) ? [first, ...rest] : undefined
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function to_result(pages: ReadonlyArray<SearchPage>): SearchResult {
|
|
97
|
-
const issues = pages.flatMap((page) =>
|
|
98
|
-
page.items.map((item) => ({
|
|
99
|
-
body: item.body ?? '',
|
|
100
|
-
labels: item.labels.map((label) => label.name),
|
|
101
|
-
})),
|
|
102
|
-
)
|
|
103
|
-
// The search API serves at most 1000 results, and says so only by a total larger than what it
|
|
104
|
-
// handed back — a window past that ceiling is measured on part of itself.
|
|
105
|
-
const total = pages[0]?.total_count ?? 0
|
|
106
|
-
const is_incomplete = pages.some((page) => page.incomplete_results)
|
|
107
|
-
|
|
108
|
-
return { issues, is_capped: is_incomplete || total > issues.length }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function search(query: string): Promise<SearchResult | undefined> {
|
|
112
|
-
try {
|
|
113
|
-
const pages = await read_pages(query)
|
|
114
|
-
|
|
115
|
-
return pages === undefined ? undefined : to_result(pages)
|
|
116
|
-
} catch {
|
|
117
|
-
return undefined
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
24
|
async function measure_uncached(
|
|
122
25
|
repo: string,
|
|
123
26
|
days: number,
|
|
124
27
|
since: string,
|
|
125
28
|
): Promise<DefectRate | undefined> {
|
|
126
29
|
const [filed, completed] = await Promise.all([
|
|
127
|
-
search(defect_rate.filed_query(repo, since)),
|
|
128
|
-
search(defect_rate.completed_query(repo, since)),
|
|
30
|
+
issue_search.search(defect_rate.filed_query(repo, since)),
|
|
31
|
+
issue_search.search(defect_rate.completed_query(repo, since)),
|
|
129
32
|
])
|
|
130
33
|
|
|
131
34
|
if (filed === undefined || completed === undefined) return undefined
|
|
@@ -158,6 +61,6 @@ async function measure_window(
|
|
|
158
61
|
)
|
|
159
62
|
}
|
|
160
63
|
|
|
161
|
-
const defect_rate_cli = { RATE_CACHE,
|
|
64
|
+
const defect_rate_cli = { RATE_CACHE, measure_window }
|
|
162
65
|
|
|
163
66
|
export { defect_rate_cli }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { run_record_path } from '#scripts/josh/run-record-path'
|
|
1
2
|
import { defect_origin_line } from './defect-origin-line'
|
|
2
3
|
|
|
3
4
|
// Defects grouped by the pull request each one's origin line names: how many per pull request, per
|
|
@@ -14,9 +15,6 @@ const SCRIPT_AREA_DEPTH = 2
|
|
|
14
15
|
const ROOT_AREA = '(root)'
|
|
15
16
|
const UNREAD_AREA = '(pull request not read)'
|
|
16
17
|
const TEST_FILE = /\.(?:test|e2e)\.ts$|\.snap$/u
|
|
17
|
-
// The run's own records — observations, accepted metrics — which nearly every merge adds, so they
|
|
18
|
-
// say nothing about what the pull request changed.
|
|
19
|
-
const RUN_RECORD_ROOT = '.josh/'
|
|
20
18
|
|
|
21
19
|
type ChangeKind = 'add' | 'fix' | 'remove'
|
|
22
20
|
|
|
@@ -54,8 +52,10 @@ interface DefectReport {
|
|
|
54
52
|
added_within_week: ReadonlyMap<number, number>
|
|
55
53
|
}
|
|
56
54
|
|
|
55
|
+
// The run's own records, which nearly every merge adds, say nothing about what the pull request
|
|
56
|
+
// changed.
|
|
57
57
|
function changed(files: ReadonlyArray<PrFile>): ReadonlyArray<PrFile> {
|
|
58
|
-
return files.filter((file) => !file.filename
|
|
58
|
+
return files.filter((file) => !run_record_path.is_run_record(file.filename))
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// A pull request that brought in a new non-test file added a mechanism; one that took out more than it
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { writeFileSync } from 'node:fs'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
7
|
+
import { bounded_pool } from '#scripts/lib/bounded-pool'
|
|
8
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
9
|
+
import { defect_rate } from './defect-rate'
|
|
10
|
+
import { effect_audit, type AuditRow, type FollowUp } from './effect-audit'
|
|
11
|
+
import { issue_sample_prime } from './issue-sample-prime'
|
|
12
|
+
import { issue_search, type SearchIssue } from './issue-search'
|
|
13
|
+
import { session_cite } from './session-cite'
|
|
14
|
+
|
|
15
|
+
// `josh effect:audit [--days <N>] [--draft <N>]` — every run-time-shortening issue closed in the window,
|
|
16
|
+
// one line each: its expected effect, its follow-up measurements and their verdict (`effect-audit.ts`).
|
|
17
|
+
// `--draft <N>` writes the retirement candidate for an issue whose measurement fell short and prints the
|
|
18
|
+
// `issue:file` line that files it once its placeholders are filled.
|
|
19
|
+
|
|
20
|
+
const ARGV_OFFSET = 2
|
|
21
|
+
const FAILURE_EXIT_CODE = 1
|
|
22
|
+
const DEFAULT_DAYS = 30
|
|
23
|
+
const PULL_CONCURRENCY = 4
|
|
24
|
+
const USAGE = 'Usage: josh effect:audit [--days <N>] [--draft <issue-number>]'
|
|
25
|
+
const OPTIONS = { days: { type: 'string' }, draft: { type: 'string' } } as const
|
|
26
|
+
|
|
27
|
+
interface AuditRequest {
|
|
28
|
+
days: number
|
|
29
|
+
draft: number | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface Audit {
|
|
33
|
+
rows: ReadonlyArray<AuditRow>
|
|
34
|
+
since: string
|
|
35
|
+
is_capped: boolean
|
|
36
|
+
unattributed: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface CitedFollowUp {
|
|
40
|
+
issue: SearchIssue
|
|
41
|
+
pr: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function positive(text: string | undefined): number | undefined {
|
|
45
|
+
const value = Number(text)
|
|
46
|
+
|
|
47
|
+
return Number.isSafeInteger(value) && value > 0 ? value : undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function with_draft(days: number, text: string | undefined): AuditRequest | undefined {
|
|
51
|
+
if (text === undefined) return { days, draft: undefined }
|
|
52
|
+
const draft = positive(text)
|
|
53
|
+
|
|
54
|
+
return draft === undefined ? undefined : { days, draft }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parse_request(argv: ReadonlyArray<string>): AuditRequest | undefined {
|
|
58
|
+
const parsed = cli_flags.values_of(argv, OPTIONS)
|
|
59
|
+
if (parsed === undefined) return undefined
|
|
60
|
+
const days = positive(parsed.days ?? String(DEFAULT_DAYS))
|
|
61
|
+
|
|
62
|
+
return days === undefined ? undefined : with_draft(days, parsed.draft)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The issue the cited pull request closes; an unreadable pull request leaves the follow-up unattributed,
|
|
66
|
+
// and the listing counts it.
|
|
67
|
+
async function measured_issue(pr: number): Promise<number | undefined> {
|
|
68
|
+
try {
|
|
69
|
+
const pull = await issue_sample_prime.read_source(pr)
|
|
70
|
+
|
|
71
|
+
return pull.closes
|
|
72
|
+
} catch {
|
|
73
|
+
return undefined
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Each cited pull request is read once, a few at a time: the follow-up search spans every date.
|
|
78
|
+
async function closes_by_pull(
|
|
79
|
+
prs: ReadonlyArray<number>,
|
|
80
|
+
): Promise<ReadonlyMap<number, number | undefined>> {
|
|
81
|
+
const unique = [...new Set(prs)]
|
|
82
|
+
const closes = await bounded_pool.bounded_map(unique, PULL_CONCURRENCY, measured_issue)
|
|
83
|
+
|
|
84
|
+
return new Map(unique.map((pr, index) => [pr, closes[index]]))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// A follow-up citing the issue form names what it measures itself; only a cited pull request is read.
|
|
88
|
+
async function follow_ups_of(candidates: ReadonlyArray<SearchIssue>): Promise<Array<FollowUp>> {
|
|
89
|
+
const cited = candidates
|
|
90
|
+
.map((issue) => ({ issue, pr: effect_audit.cited_pull(issue) }))
|
|
91
|
+
.filter((entry): entry is CitedFollowUp => entry.pr !== undefined)
|
|
92
|
+
const closes = await closes_by_pull(cited.map(({ pr }) => pr))
|
|
93
|
+
const by_issue = candidates
|
|
94
|
+
.map((issue) => ({ issue, measures: effect_audit.cited_issue(issue) }))
|
|
95
|
+
.filter(({ measures }) => measures !== undefined)
|
|
96
|
+
|
|
97
|
+
return [...cited.map(({ issue, pr }) => ({ issue, measures: closes.get(pr) })), ...by_issue]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function read_audit(repo: string, days: number, now_ms: number): Promise<Audit | undefined> {
|
|
101
|
+
const since = defect_rate.window_start(now_ms, days)
|
|
102
|
+
const [origins, follow_ups] = await Promise.all([
|
|
103
|
+
issue_search.search(effect_audit.origin_query(repo, since)),
|
|
104
|
+
issue_search.search(effect_audit.follow_up_query(repo)),
|
|
105
|
+
])
|
|
106
|
+
|
|
107
|
+
if (origins === undefined || follow_ups === undefined) return undefined
|
|
108
|
+
|
|
109
|
+
const linked = await follow_ups_of(follow_ups.issues)
|
|
110
|
+
const rows = effect_audit.rows(origins.issues, linked)
|
|
111
|
+
const unattributed = linked.filter((follow_up) => follow_up.measures === undefined).length
|
|
112
|
+
|
|
113
|
+
return { rows, since, is_capped: origins.is_capped || follow_ups.is_capped, unattributed }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function print_audit(audit: Audit): number {
|
|
117
|
+
console.info(effect_audit.summary(audit.rows, audit.since))
|
|
118
|
+
for (const row of audit.rows) console.info(session_cite.text(effect_audit.format_row(row)))
|
|
119
|
+
if (audit.is_capped) console.info('(capped: the search served part of the window only)')
|
|
120
|
+
|
|
121
|
+
if (audit.unattributed > 0) {
|
|
122
|
+
console.info(
|
|
123
|
+
`(${String(audit.unattributed)} follow-ups measure no issue: their pull request could not be read or closes none)`,
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return 0
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function print_draft(rows: ReadonlyArray<AuditRow>, issue: number): number {
|
|
131
|
+
const row = rows.find((candidate) => candidate.origin.number === issue)
|
|
132
|
+
|
|
133
|
+
if (row?.result !== 'unmet') {
|
|
134
|
+
console.error(
|
|
135
|
+
`${session_cite.issue(issue)} is not an issue of the window whose measurement fell short`,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return FAILURE_EXIT_CODE
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const file = path.join(tmpdir(), `josh-effect-retire-${String(issue)}.md`)
|
|
142
|
+
|
|
143
|
+
writeFileSync(file, effect_audit.draft_body(row))
|
|
144
|
+
console.info(`Fill in the <...> lines of ${file}, then file it:`)
|
|
145
|
+
console.info(
|
|
146
|
+
`pnpm josh issue:file "${effect_audit.draft_title(row)}" --body-file ${file} --label run:lane`,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function audit_of(request: AuditRequest, now_ms: number): Promise<Audit | undefined> {
|
|
153
|
+
const repo = await git_gh_command.repo_get_name_with_owner()
|
|
154
|
+
|
|
155
|
+
return repo === undefined ? undefined : await read_audit(repo, request.days, now_ms)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function run(argv: ReadonlyArray<string>, now_ms: number = Date.now()): Promise<number> {
|
|
159
|
+
const request = parse_request(argv)
|
|
160
|
+
if (request === undefined) return (console.error(USAGE), FAILURE_EXIT_CODE)
|
|
161
|
+
const audit = await audit_of(request, now_ms)
|
|
162
|
+
|
|
163
|
+
if (audit === undefined) {
|
|
164
|
+
return (console.error('effect:audit: the issue search could not be read'), FAILURE_EXIT_CODE)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return request.draft === undefined ? print_audit(audit) : print_draft(audit.rows, request.draft)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
171
|
+
process.exitCode = await run(argv)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const effect_audit_cli = { run }
|
|
175
|
+
|
|
176
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
|
|
177
|
+
|
|
178
|
+
export { effect_audit_cli }
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { expected_effect_lint } from './expected-effect-lint'
|
|
2
|
+
import { issue_cite } from './issue-cite'
|
|
3
|
+
import { has_label_name, MEASURED_LABEL, MEASURED_UNMET_LABEL } from './issue-labels'
|
|
4
|
+
import { issue_sample_gate } from './issue-sample-gate'
|
|
5
|
+
import type { SearchIssue } from './issue-search'
|
|
6
|
+
|
|
7
|
+
// joshuafolkken/kit#4958: a run-time-shortening issue states its expected effect at filing
|
|
8
|
+
// (`expected-effect-lint.ts`), and nothing read it again once merged. This sets each closed one beside
|
|
9
|
+
// the follow-up measurements of it — issues whose run condition names the pull request that closed it
|
|
10
|
+
// — and their recorded verdict: `measured:unmet` fell short, `measured` alone reached it, anything else
|
|
11
|
+
// is not measured yet. A shortfall is drafted as a retirement candidate for `issue:file`.
|
|
12
|
+
|
|
13
|
+
const EFFECT_LABELS: ReadonlyArray<string> = ['効く率', '1 回あたりの得', '外れたときの損']
|
|
14
|
+
const NO_VALUE = '—'
|
|
15
|
+
const RESULTS = ['met', 'unmet', 'unmeasured'] as const
|
|
16
|
+
|
|
17
|
+
type EffectResult = (typeof RESULTS)[number]
|
|
18
|
+
|
|
19
|
+
interface AuditRow {
|
|
20
|
+
origin: SearchIssue
|
|
21
|
+
follow_ups: ReadonlyArray<SearchIssue>
|
|
22
|
+
result: EffectResult
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A follow-up and the issue it measures: the one its run condition's pull request closes. A body
|
|
26
|
+
// naming other issues (a parent, lanes it compares against) measures none of them.
|
|
27
|
+
interface FollowUp {
|
|
28
|
+
issue: SearchIssue
|
|
29
|
+
measures: number | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const RESULT_WORDS: Readonly<Record<EffectResult, string>> = {
|
|
33
|
+
met: '届いた',
|
|
34
|
+
unmet: '届かなかった',
|
|
35
|
+
unmeasured: '未計測',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Plain: the rows feed an issue body as well as the session, which links them where it prints them.
|
|
39
|
+
function cite(issue: SearchIssue): string {
|
|
40
|
+
return issue_cite.plain(issue.number)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The closed time-shortening issues of the window. The search narrows by the declaration's words; the
|
|
44
|
+
// exact line under the background heading is checked on each body, since the search matches the
|
|
45
|
+
// follow-ups' own "confirm the shortening" aim too.
|
|
46
|
+
function origin_query(repo: string, since: string): string {
|
|
47
|
+
return `repo:${repo} is:issue is:closed reason:completed closed:>=${since} "目的: 時間短縮" in:body`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Every follow-up measurement, of any date: one may be filed before or long after its origin closed.
|
|
51
|
+
function follow_up_query(repo: string): string {
|
|
52
|
+
return `repo:${repo} is:issue "実施可能条件" in:body`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The pull request a follow-up's run condition names, or `undefined` for an issue that declares none
|
|
56
|
+
// or cites the issue whose closing pull request it waits on.
|
|
57
|
+
function cited_pull(issue: SearchIssue): number | undefined {
|
|
58
|
+
return issue_sample_gate.cited_via(issue.body, 'pull')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The issue a follow-up's run condition names by the issue form — the issue it measures, unread.
|
|
62
|
+
function cited_issue(issue: SearchIssue): number | undefined {
|
|
63
|
+
return issue_sample_gate.cited_via(issue.body, 'issue')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// One shortfall outweighs any reach: a mechanism some measurement found wanting is the one to look at.
|
|
67
|
+
function result_of(follow_ups: ReadonlyArray<SearchIssue>): EffectResult {
|
|
68
|
+
if (follow_ups.some((issue) => has_label_name(issue.labels, MEASURED_UNMET_LABEL))) return 'unmet'
|
|
69
|
+
|
|
70
|
+
return follow_ups.some((issue) => has_label_name(issue.labels, MEASURED_LABEL))
|
|
71
|
+
? 'met'
|
|
72
|
+
: 'unmeasured'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function row_of(origin: SearchIssue, follow_ups: ReadonlyArray<FollowUp>): AuditRow {
|
|
76
|
+
const measuring = follow_ups
|
|
77
|
+
.filter((follow_up) => follow_up.measures === origin.number)
|
|
78
|
+
.map((follow_up) => follow_up.issue)
|
|
79
|
+
|
|
80
|
+
return { origin, follow_ups: measuring, result: result_of(measuring) }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function rows(
|
|
84
|
+
origins: ReadonlyArray<SearchIssue>,
|
|
85
|
+
follow_ups: ReadonlyArray<FollowUp>,
|
|
86
|
+
): ReadonlyArray<AuditRow> {
|
|
87
|
+
return origins
|
|
88
|
+
.filter((origin) => expected_effect_lint.is_target(origin.body))
|
|
89
|
+
.map((origin) => row_of(origin, follow_ups))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function effect_text(body: string): string {
|
|
93
|
+
return EFFECT_LABELS.map(
|
|
94
|
+
(label) => `${label} ${expected_effect_lint.value_of(body, label) ?? NO_VALUE}`,
|
|
95
|
+
).join(' · ')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function follow_up_text(follow_ups: ReadonlyArray<SearchIssue>): string {
|
|
99
|
+
if (follow_ups.length === 0) return 'なし'
|
|
100
|
+
|
|
101
|
+
return follow_ups.map((issue) => `${cite(issue)} (${issue.state})`).join(', ')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function format_row(row: AuditRow): string {
|
|
105
|
+
const effect = `期待効果: ${effect_text(row.origin.body)}`
|
|
106
|
+
const measured = `後続計測: ${follow_up_text(row.follow_ups)}`
|
|
107
|
+
|
|
108
|
+
return `${cite(row.origin)} — ${effect} — ${measured} — 結果: ${RESULT_WORDS[row.result]}`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function tally(audit_rows: ReadonlyArray<AuditRow>, result: EffectResult): string {
|
|
112
|
+
return `${RESULT_WORDS[result]} ${String(audit_rows.filter((row) => row.result === result).length)}`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function summary(audit_rows: ReadonlyArray<AuditRow>, since: string): string {
|
|
116
|
+
const tallies = RESULTS.map((result) => tally(audit_rows, result)).join(' · ')
|
|
117
|
+
|
|
118
|
+
return `effect:audit — closed since ${since}: ${String(audit_rows.length)} issues · ${tallies}`
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function unmet_measurements(row: AuditRow): string {
|
|
122
|
+
return row.follow_ups
|
|
123
|
+
.filter((issue) => has_label_name(issue.labels, MEASURED_UNMET_LABEL))
|
|
124
|
+
.map((issue) => cite(issue))
|
|
125
|
+
.join(', ')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function draft_title(row: AuditRow): string {
|
|
129
|
+
return `Retire or shrink the mechanism of ${cite(row.origin)}, which fell short of its expected effect`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function background_lines(origin: string, measurements: string): ReadonlyArray<string> {
|
|
133
|
+
return [
|
|
134
|
+
'## 背景',
|
|
135
|
+
'',
|
|
136
|
+
'- 種別: 非不具合',
|
|
137
|
+
'- 目的: 機能改善',
|
|
138
|
+
'- 人の判断: 不要',
|
|
139
|
+
'- 優先度: 通常',
|
|
140
|
+
'- 規模見積もり: 約 <N> ファイル / <M> 行(テスト除く)',
|
|
141
|
+
`- 出どころ: ${measurements} の計測(\`measured:unmet\`)`,
|
|
142
|
+
'',
|
|
143
|
+
`${origin} で足した機構は、後続計測 ${measurements} で期待効果に届かなかった。効かない機構は利用者の実行時間・読む量・保守の負担を増やすので、撤去するか、効く範囲に縮小する。`,
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The retirement candidate's body. The scale line is what only the filer knows: `issue:lint` refuses
|
|
148
|
+
// it until it is filled, and `issue:file`'s scout stops a duplicate.
|
|
149
|
+
function draft_body(row: AuditRow): string {
|
|
150
|
+
const origin = cite(row.origin)
|
|
151
|
+
const measurements = unmet_measurements(row)
|
|
152
|
+
|
|
153
|
+
return [
|
|
154
|
+
...background_lines(origin, measurements),
|
|
155
|
+
'',
|
|
156
|
+
'## 現象',
|
|
157
|
+
'',
|
|
158
|
+
`- ${origin} の期待効果: ${effect_text(row.origin.body)}`,
|
|
159
|
+
`- 計測 ${measurements} の結果: 届かなかった`,
|
|
160
|
+
'',
|
|
161
|
+
'## 期待結果',
|
|
162
|
+
'',
|
|
163
|
+
`- ${origin} で足した機構が撤去されているか、効く範囲に縮小されている`,
|
|
164
|
+
'',
|
|
165
|
+
'## 受け入れ条件',
|
|
166
|
+
'',
|
|
167
|
+
`- [ ] ${origin} で足した機構を撤去するか縮小し、どちらを選んだかとその理由を記録する`,
|
|
168
|
+
'',
|
|
169
|
+
].join('\n')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const effect_audit = {
|
|
173
|
+
cited_issue,
|
|
174
|
+
cited_pull,
|
|
175
|
+
draft_body,
|
|
176
|
+
draft_title,
|
|
177
|
+
follow_up_query,
|
|
178
|
+
format_row,
|
|
179
|
+
origin_query,
|
|
180
|
+
rows,
|
|
181
|
+
summary,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type { AuditRow, EffectResult, FollowUp }
|
|
185
|
+
export { effect_audit }
|
|
@@ -9,7 +9,18 @@ import { markdown_section } from './markdown-section'
|
|
|
9
9
|
const DECLARATION_LINE = '- 目的: 時間短縮'
|
|
10
10
|
const BACKGROUND_HEADING = '## 背景'
|
|
11
11
|
const HEADING = '## 期待効果'
|
|
12
|
-
const
|
|
12
|
+
const RATE_LABEL = '効く率'
|
|
13
|
+
const GAIN_LABEL = '1 回あたりの得'
|
|
14
|
+
const ITEM_LABELS: ReadonlyArray<string> = [RATE_LABEL, GAIN_LABEL, '外れたときの損', '出典']
|
|
15
|
+
// The numbers the backlog ranking reads off the rate and the gain (joshuafolkken/kit#5302).
|
|
16
|
+
const WEEK = '週'
|
|
17
|
+
const COUNT = /(?<![\d.])(?<count>\d+) ?回/gu
|
|
18
|
+
const SPAN = /(?<![\d.])(?<weeks>\d+) ?週/u
|
|
19
|
+
const ONE_WEEK = 1
|
|
20
|
+
const DURATION = /(?<![\d.])(?<amount>\d+(?:\.\d+)?) ?(?<unit>[分秒])/u
|
|
21
|
+
const SECONDS_UNIT = '秒'
|
|
22
|
+
const SECONDS_PER_MINUTE = 60
|
|
23
|
+
const NONE = 0
|
|
13
24
|
// A template placeholder value — the `<...>` guidance a filed Issue replaces. It is not an estimate, but a
|
|
14
25
|
// value that only opens with `<` (an upper bound such as `<1 min`) is one, so the placeholder must close with `>` too.
|
|
15
26
|
const PLACEHOLDER = /^<.*>$/u
|
|
@@ -29,22 +40,64 @@ function item_value(line: string, label: string): string | undefined {
|
|
|
29
40
|
return line.slice(prefix.length).trim()
|
|
30
41
|
}
|
|
31
42
|
|
|
32
|
-
function
|
|
33
|
-
return
|
|
34
|
-
|
|
43
|
+
function is_stated(value: string | undefined): value is string {
|
|
44
|
+
return value !== undefined && value.length > 0 && !PLACEHOLDER.test(value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function stated_value(lines: ReadonlyArray<string>, label: string): string | undefined {
|
|
48
|
+
return lines.map((line) => item_value(line.trim(), label)).find((value) => is_stated(value))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function effect_lines(body: string): ReadonlyArray<string> {
|
|
52
|
+
const section = markdown_section.section_lines(body, HEADING).join('\n')
|
|
53
|
+
|
|
54
|
+
return markdown_section.unfenced_lines(section)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The value the body's expected-effect section states for one item, or undefined when it states none.
|
|
58
|
+
function value_of(body: string, label: string): string | undefined {
|
|
59
|
+
return stated_value(effect_lines(body), label)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The weeks a rate is counted over: its stated span ("10 in 2 weeks", "1 every 2 weeks"), else one.
|
|
63
|
+
function weeks_of(rate: string): number {
|
|
64
|
+
const weeks = SPAN.exec(rate)?.groups?.['weeks']
|
|
35
65
|
|
|
36
|
-
|
|
37
|
-
|
|
66
|
+
return Math.max(Number(weeks ?? ONE_WEEK), ONE_WEEK)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The hits a week a rate states: its last count, so "5 of 23 a week" reads the 5 the speed-up applies to,
|
|
70
|
+
// over its span of weeks. `0` when the rate names no week or no count.
|
|
71
|
+
function weekly_hits(rate: string | undefined): number {
|
|
72
|
+
if (!rate?.includes(WEEK)) return NONE
|
|
73
|
+
const counts = [...rate.matchAll(COUNT)].map((match) => Number(match.groups?.['count']))
|
|
74
|
+
|
|
75
|
+
return (counts.at(-1) ?? NONE) / weeks_of(rate)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The minutes one hit saves: the first number the gain states in seconds or minutes — a range written
|
|
79
|
+
// with one closing unit ("3-6 min") reads its upper bound. `0` when it states none.
|
|
80
|
+
function minutes_per_hit(gain = ''): number {
|
|
81
|
+
const groups = DURATION.exec(gain)?.groups
|
|
82
|
+
if (groups === undefined) return NONE
|
|
83
|
+
const amount = Number(groups['amount'])
|
|
84
|
+
|
|
85
|
+
return groups['unit'] === SECONDS_UNIT ? amount / SECONDS_PER_MINUTE : amount
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// The minutes a week the declared speed-up saves — hits a week times the gain per hit — on the same
|
|
89
|
+
// minute scale as `issue_priority.lost_minutes`. `0` when the rate or the gain is not a number.
|
|
90
|
+
function weekly_minutes(body = ''): number {
|
|
91
|
+
return weekly_hits(value_of(body, RATE_LABEL)) * minutes_per_hit(value_of(body, GAIN_LABEL))
|
|
38
92
|
}
|
|
39
93
|
|
|
40
94
|
// Every expected-effect problem a body has, or an empty array. A body without the declaration has none.
|
|
41
95
|
function problems(body: string): ReadonlyArray<string> {
|
|
42
96
|
if (!is_target(body)) return []
|
|
43
97
|
if (!markdown_section.has_line(body, HEADING)) return [`missing heading: ${HEADING}`]
|
|
44
|
-
const
|
|
45
|
-
const lines = markdown_section.unfenced_lines(section)
|
|
98
|
+
const lines = effect_lines(body)
|
|
46
99
|
|
|
47
|
-
return ITEM_LABELS.filter((label) =>
|
|
100
|
+
return ITEM_LABELS.filter((label) => stated_value(lines, label) === undefined).map(
|
|
48
101
|
(label) => `missing expected-effect item: - ${label}: <value>`,
|
|
49
102
|
)
|
|
50
103
|
}
|
|
@@ -55,6 +108,8 @@ const expected_effect_lint = {
|
|
|
55
108
|
ITEM_LABELS,
|
|
56
109
|
is_target,
|
|
57
110
|
problems,
|
|
111
|
+
value_of,
|
|
112
|
+
weekly_minutes,
|
|
58
113
|
}
|
|
59
114
|
|
|
60
115
|
export { expected_effect_lint }
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
2
|
import { open_issue_schema, type OpenIssueData } from '#scripts/git/git-schemas'
|
|
3
3
|
import { parse_json_array_safe } from '#scripts/git/parse-json-array'
|
|
4
|
+
import { expected_effect_lint } from './expected-effect-lint'
|
|
4
5
|
import { has_any_label, NOT_DIRECTLY_RUNNABLE_LABELS } from './issue-labels'
|
|
6
|
+
import { issue_priority } from './issue-priority'
|
|
5
7
|
import { issue_rank, type DependencyRow } from './issue-rank'
|
|
6
8
|
import { session_cite } from './session-cite'
|
|
7
9
|
|
|
@@ -63,7 +65,10 @@ function order(
|
|
|
63
65
|
const dependents = issue_rank.count_dependents(candidates.map((issue) => dependency_row(issue)))
|
|
64
66
|
|
|
65
67
|
return issue_rank.rank(candidates, (issue) => ({
|
|
68
|
+
is_last: issue_priority.is_last(issue.body),
|
|
66
69
|
labels: (issue.labels ?? []).map((label) => label.name),
|
|
70
|
+
lost_minutes: issue_priority.lost_minutes(issue.body),
|
|
71
|
+
gain_minutes: expected_effect_lint.weekly_minutes(issue.body),
|
|
67
72
|
dependents: dependents.get(String(issue.number)) ?? 0,
|
|
68
73
|
}))
|
|
69
74
|
}
|