@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,307 @@
|
|
|
1
|
+
import { gate_tree, type GateTree } from '#scripts/gate/gate-tree'
|
|
2
|
+
import { moved_paths, type MovedParts } from '#scripts/gate/moved-paths'
|
|
3
|
+
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
4
|
+
import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
|
|
5
|
+
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import { release_approval_prune } from '#scripts/release/release-approval-prune'
|
|
7
|
+
import { test_declared_logic } from '#scripts/test/test-declared-logic'
|
|
8
|
+
import { review_attest } from './review-attest'
|
|
9
|
+
import { review_checkout, type ReviewCheckout } from './review-checkout'
|
|
10
|
+
import { review_round_one_blobs } from './review-round-one-blobs'
|
|
11
|
+
import { review_round2 } from './review-round2'
|
|
12
|
+
import { review_stamps } from './review-stamps'
|
|
13
|
+
|
|
14
|
+
// The tree a recorded `/code-review` round stands for: the changed files as they were when
|
|
15
|
+
// `review:record` wrote the round, its fixes included (joshuafolkken/kit#4620). A lane reviews before
|
|
16
|
+
// `josh ship`, so the ship's round 1 asks this record whether the tree moved since: unmoved, the
|
|
17
|
+
// recorded round is the ship's; moved only where round 2 reads, it is still the ship's and round 2
|
|
18
|
+
// reads the delta (joshuafolkken/kit#4878); else round 1 reads the delta from here.
|
|
19
|
+
//
|
|
20
|
+
// **Taken at the record, not the brief**. A round that fixed findings approved its own edits, so the
|
|
21
|
+
// briefed tree is not what its verdict covers: a file reverted to the briefed bytes would drop out of a
|
|
22
|
+
// delta measured from there with its finding back in it.
|
|
23
|
+
|
|
24
|
+
const LINE_SUFFIX = /:\d+(?:-\d+)?$/u
|
|
25
|
+
// No file's digest: a path recorded with it never matches the tree, whether the change holds it or not.
|
|
26
|
+
const UNSETTLED_DIGEST = 'unsettled'
|
|
27
|
+
|
|
28
|
+
// **A file a recorded High cites is recorded as unsettled**: a High is never fixed in place, so the
|
|
29
|
+
// round's verdict does not stand for that file. Its digest matches no tree, so it always reads as moved
|
|
30
|
+
// — the tree is never held, and round 2 reads its fix where its delta holds it (joshuafolkken/kit#4941);
|
|
31
|
+
// anywhere else round 1 reads it again rather than passing over the finding it still holds. A file
|
|
32
|
+
// outside the change (a reader the change broke) is recorded too: left out, it would be absent on both
|
|
33
|
+
// sides and read as unmoved.
|
|
34
|
+
function settled_files(
|
|
35
|
+
files: Record<string, string>,
|
|
36
|
+
unsettled: ReadonlyArray<string>,
|
|
37
|
+
): Record<string, string> {
|
|
38
|
+
const cited = unsettled.map((file): [string, string] => [
|
|
39
|
+
file.replace(LINE_SUFFIX, ''),
|
|
40
|
+
UNSETTLED_DIGEST,
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
return { ...files, ...Object.fromEntries(cited) }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Swallowed: no record re-runs the ship's round 1 over the whole change — wider, never narrower. The
|
|
47
|
+
// blobs let a sync's change to a covered document carry onto the new base (`carried_paths`).
|
|
48
|
+
async function record(unsettled: ReadonlyArray<string> = []): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
const { files, base } = await gate_tree.read_gate_tree()
|
|
51
|
+
const blobs = review_round_one_blobs.capture(Object.keys(files))
|
|
52
|
+
|
|
53
|
+
review_stamps.reviewed_stamp.write(settled_files(files, unsettled), undefined, base, blobs)
|
|
54
|
+
} catch {
|
|
55
|
+
/* no record widens the next round rather than narrowing it */
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// An empty tree or an unresolved base is git not answering, never "nothing moved".
|
|
60
|
+
//
|
|
61
|
+
// **A base the ship's sync moved is unmoved for every path the default branch left alone**
|
|
62
|
+
// (joshuafolkken/kit#4829): with every recorded digest matching, only a synced path the round covers
|
|
63
|
+
// changes what it read — unless round 2 would not execute it and main alone moved it
|
|
64
|
+
// (joshuafolkken/kit#4984). Main's other paths are the scoped pair's, the gate's and CI's to check.
|
|
65
|
+
//
|
|
66
|
+
// **A `josh metrics` approval the branch rewrote is no move** (joshuafolkken/kit#4788): the ship's
|
|
67
|
+
// pre-detach re-record writes it after `review:record`, and it is data `josh metrics` derives and the
|
|
68
|
+
// gate's own metrics step checks, never what the round reviewed. An approval a recorded High cites
|
|
69
|
+
// stays unsettled, so it still reads as moved.
|
|
70
|
+
function is_rerecorded_approval(stamp: FileMapStamp, path: string): boolean {
|
|
71
|
+
return release_approval_prune.is_approval(path) && stamp.files[path] !== UNSETTLED_DIGEST
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// **A test added or edited since the record is no move** (joshuafolkken/kit#4874): `chain-rule.md`
|
|
75
|
+
// step 0 pins each `test:pinned` unpinned place after `review:record`, and a test changes no runtime
|
|
76
|
+
// the round's verdict covers; round 2 still reads the test delta (`review-round2.ts` keeps tests live).
|
|
77
|
+
// A test removed since, or one a recorded High cites, still reads as moved.
|
|
78
|
+
function is_pinning_test(stamp: FileMapStamp, tree: GateTree, path: string): boolean {
|
|
79
|
+
const is_settled = Object.hasOwn(tree.files, path) && stamp.files[path] !== UNSETTLED_DIGEST
|
|
80
|
+
|
|
81
|
+
return is_settled && test_declared_logic.is_test_file(path)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function is_no_move(stamp: FileMapStamp, tree: GateTree, path: string): boolean {
|
|
85
|
+
return is_rerecorded_approval(stamp, path) || is_pinning_test(stamp, tree, path)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// How the tree stands against the record: `held` — nothing the round read moved; `delta` — every move
|
|
89
|
+
// is one round 2 reads (joshuafolkken/kit#4878); `unsettled` — any other move, or git did not answer.
|
|
90
|
+
const MOVEMENT = { DELTA: 'delta', HELD: 'held', UNSETTLED: 'unsettled' } as const
|
|
91
|
+
|
|
92
|
+
type Movement = (typeof MOVEMENT)[keyof typeof MOVEMENT]
|
|
93
|
+
|
|
94
|
+
// The movement, and on `unsettled` the condition that decided it, with the path that met it where one
|
|
95
|
+
// did — printed by `review:record --check` so a lane reads why the ship reviews round 1 again
|
|
96
|
+
// (joshuafolkken/kit#4941).
|
|
97
|
+
interface Standing {
|
|
98
|
+
movement: Movement
|
|
99
|
+
cause?: string
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const CAUSE = {
|
|
103
|
+
NO_RECORD: 'no reviewed tree was recorded',
|
|
104
|
+
SKIPPED:
|
|
105
|
+
'a move round 2 would not read — documentation, a prompt, an inert path, a revert to the briefed bytes, or a file outside the change',
|
|
106
|
+
SYNCED: 'the default branch changed a file the round covers',
|
|
107
|
+
UNFIXED:
|
|
108
|
+
"a file a recorded High cites still holds its round-1 brief bytes, so no fix is in round 2's delta",
|
|
109
|
+
UNATTESTED: 'the round was not attested on this branch',
|
|
110
|
+
UNREAD: 'git did not answer',
|
|
111
|
+
} as const
|
|
112
|
+
|
|
113
|
+
function unsettled_by(cause: string, path?: string): Standing {
|
|
114
|
+
return { movement: MOVEMENT.UNSETTLED, cause: path === undefined ? cause : `${cause}: ${path}` }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// **A covered file only the default branch moved is no move where round 2 would not execute it**
|
|
118
|
+
// (joshuafolkken/kit#4984): documentation, a prompt or an inert path — a generated usage heading main
|
|
119
|
+
// rewrote beside the lane's own. Its recorded blob carried onto the new base reproduces the file now
|
|
120
|
+
// only where main's change is all that moved it (`review-round-one-blobs.ts`); a lane edit since the
|
|
121
|
+
// record, a carry git cannot make, or a path a recorded High cites stays moved.
|
|
122
|
+
function is_carry_candidate(stamp: FileMapStamp, path: string): boolean {
|
|
123
|
+
const is_settled = Object.hasOwn(stamp.files, path) && stamp.files[path] !== UNSETTLED_DIGEST
|
|
124
|
+
|
|
125
|
+
return is_settled && review_round2.is_unexecuted(path, [])
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function carried_paths(stamp: FileMapStamp, tree: GateTree, parts: MovedParts): Array<string> {
|
|
129
|
+
const candidates = parts.synced.filter((path) => is_carry_candidate(stamp, path))
|
|
130
|
+
const { blobs, base: from } = stamp
|
|
131
|
+
const delta =
|
|
132
|
+
candidates.length === 0
|
|
133
|
+
? undefined
|
|
134
|
+
: review_round_one_blobs.carried_delta({ blobs, from, to: tree.base, paths: candidates })
|
|
135
|
+
|
|
136
|
+
return delta === undefined ? [] : candidates.filter((path) => !delta.includes(path))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function moved_files(stamp: FileMapStamp, tree: GateTree, parts: MovedParts): Array<string> {
|
|
140
|
+
return parts.changed.filter((path) => !is_no_move(stamp, tree, path))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// **Round 2 reads a move only where its own delta holds it**: it diffs from the round-1 brief's stamp
|
|
144
|
+
// (`briefed`) and passes over documentation, prompts and inert paths (`review-round2.ts`), so a file
|
|
145
|
+
// reverted to the briefed bytes, or one round 2 skips, would ship unread. A path a recorded High cites
|
|
146
|
+
// is read there too — its fix is the delta round 2 verifies, and its brief names the file as a High's
|
|
147
|
+
// fix (`review-brief.ts`). A default-branch change to a covered file is outside round 2's carried delta.
|
|
148
|
+
function is_read_by_round_two(
|
|
149
|
+
path: string,
|
|
150
|
+
tree: GateTree,
|
|
151
|
+
briefed: FileMapStamp | undefined,
|
|
152
|
+
): boolean {
|
|
153
|
+
if (review_round2.is_unexecuted(path, [])) return false
|
|
154
|
+
|
|
155
|
+
return briefed?.files[path] !== tree.files[path]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// An unread High-cited file round 2 would execute is one whose fix has not landed, not a revert.
|
|
159
|
+
function unread_cause(stamp: FileMapStamp, path: string): string {
|
|
160
|
+
const is_cited = stamp.files[path] === UNSETTLED_DIGEST
|
|
161
|
+
|
|
162
|
+
return is_cited && !review_round2.is_unexecuted(path, []) ? CAUSE.UNFIXED : CAUSE.SKIPPED
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function moved_standing(
|
|
166
|
+
stamp: FileMapStamp,
|
|
167
|
+
moved: ReadonlyArray<string>,
|
|
168
|
+
tree: GateTree,
|
|
169
|
+
briefed: FileMapStamp | undefined,
|
|
170
|
+
): Standing {
|
|
171
|
+
if (moved.length === 0) return { movement: MOVEMENT.HELD }
|
|
172
|
+
|
|
173
|
+
const unread = moved.find((path) => !is_read_by_round_two(path, tree, briefed))
|
|
174
|
+
|
|
175
|
+
if (unread === undefined) return { movement: MOVEMENT.DELTA }
|
|
176
|
+
|
|
177
|
+
return unsettled_by(unread_cause(stamp, unread), unread)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function standing(
|
|
181
|
+
stamp: FileMapStamp,
|
|
182
|
+
tree: GateTree,
|
|
183
|
+
briefed?: FileMapStamp,
|
|
184
|
+
): Promise<Standing> {
|
|
185
|
+
const parts = await moved_paths.moved_parts(stamp, tree)
|
|
186
|
+
|
|
187
|
+
if (parts === undefined) return unsettled_by(CAUSE.UNREAD)
|
|
188
|
+
|
|
189
|
+
const carried = carried_paths(stamp, tree, parts)
|
|
190
|
+
const synced = parts.synced.find(
|
|
191
|
+
(path) => Object.hasOwn(stamp.files, path) && !carried.includes(path),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
if (synced !== undefined) return unsettled_by(CAUSE.SYNCED, synced)
|
|
195
|
+
|
|
196
|
+
const moved = moved_files(stamp, tree, parts).filter((path) => !carried.includes(path))
|
|
197
|
+
|
|
198
|
+
return moved_standing(stamp, moved, tree, briefed)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function movement(
|
|
202
|
+
stamp: FileMapStamp,
|
|
203
|
+
tree: GateTree,
|
|
204
|
+
briefed?: FileMapStamp,
|
|
205
|
+
): Promise<Movement> {
|
|
206
|
+
const answer = await standing(stamp, tree, briefed)
|
|
207
|
+
|
|
208
|
+
return answer.movement
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function is_unmoved(stamp: FileMapStamp, tree: GateTree): Promise<boolean> {
|
|
212
|
+
return (await movement(stamp, tree)) === MOVEMENT.HELD
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// **Only a review attested on this branch is reused**, as `resume_point` narrows from no other: the
|
|
216
|
+
// reuse skips the ship's own `review:attest --check`, so a round recorded over a review that read
|
|
217
|
+
// another checkout, or never finished, would otherwise ship unreviewed.
|
|
218
|
+
async function standing_here(): Promise<Standing> {
|
|
219
|
+
const stamp = review_stamps.reviewed_stamp.read()
|
|
220
|
+
|
|
221
|
+
if (stamp === undefined) return unsettled_by(CAUSE.NO_RECORD)
|
|
222
|
+
|
|
223
|
+
const checkout = await review_checkout.read_checkout()
|
|
224
|
+
|
|
225
|
+
if (!review_attest.is_attested_on(checkout, checkout.root)) return unsettled_by(CAUSE.UNATTESTED)
|
|
226
|
+
|
|
227
|
+
const tree = await gate_tree.read_gate_tree()
|
|
228
|
+
|
|
229
|
+
return await standing(stamp, tree, review_stamps.round_one_stamp.read())
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// The files a recorded High cites — the ones the round-2 brief names as a High's fix to read whole.
|
|
233
|
+
function high_cited(): ReadonlyArray<string> {
|
|
234
|
+
const files = review_stamps.reviewed_stamp.read()?.files ?? {}
|
|
235
|
+
|
|
236
|
+
return Object.keys(files).filter((path) => files[path] === UNSETTLED_DIGEST)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// The record a resumed brief narrows from: written after the brief the pointer names, whose review was
|
|
240
|
+
// attested on this branch. Asked before the next brief mints its pointer, which this record then
|
|
241
|
+
// predates — so it narrows one round, never two.
|
|
242
|
+
function resume_point(checkout: ReviewCheckout): FileMapStamp | undefined {
|
|
243
|
+
const stamp = review_stamps.reviewed_stamp.read()
|
|
244
|
+
|
|
245
|
+
if (stamp === undefined || !review_attest.is_briefed_since(stamp.taken_at, checkout.root)) {
|
|
246
|
+
return undefined
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return review_attest.is_attested_on(checkout, checkout.root) ? stamp : undefined
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// **A ship's round 1 that stopped on a finding stays its round 1 however the tree moves since**: the
|
|
253
|
+
// tree moves by the fix, and the fix delta is round 2's to verify (joshuafolkken/kit#2964). Reviewed
|
|
254
|
+
// as round 1 again, every stop would cost one more review and one more chance to stop. The marker
|
|
255
|
+
// names the issue, so a checkout that ships another issue next never reuses this one's round.
|
|
256
|
+
const STOPPED_PREFIX = 'josh-review-round1-stopped-'
|
|
257
|
+
|
|
258
|
+
function stopped_path(): string {
|
|
259
|
+
return stamp_file.stamp_path(STOPPED_PREFIX, PROJECT_ROOT)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Written when the ship's round 1 stops on a finding, removed when it ends any other way. A write the
|
|
263
|
+
// temp directory refuses is swallowed: no marker re-runs round 1 — wider, never narrower.
|
|
264
|
+
function mark_stopped(issue: number, is_stopped: boolean): void {
|
|
265
|
+
if (!is_stopped) {
|
|
266
|
+
stamp_file.remove_stamp(stopped_path())
|
|
267
|
+
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
stamp_file.write_text_stamp(stopped_path(), String(issue))
|
|
273
|
+
} catch {
|
|
274
|
+
/* no marker reviews round 1 again rather than passing over it */
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function is_stopped_on(issue: number): boolean {
|
|
279
|
+
return stamp_file.read_stamp_text(stopped_path()) === String(issue)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// The ship drops both records when the issue changed after the recorded round's brief: that round never
|
|
283
|
+
// read the new specification, so the next one reviews the whole change.
|
|
284
|
+
function discard(): void {
|
|
285
|
+
try {
|
|
286
|
+
review_stamps.reviewed_stamp.remove()
|
|
287
|
+
stamp_file.remove_stamp(stopped_path())
|
|
288
|
+
} catch {
|
|
289
|
+
/* the temp directory refused the removal; nothing else here can */
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const review_reviewed_tree = {
|
|
294
|
+
MOVEMENT,
|
|
295
|
+
discard,
|
|
296
|
+
high_cited,
|
|
297
|
+
is_stopped_on,
|
|
298
|
+
is_unmoved,
|
|
299
|
+
mark_stopped,
|
|
300
|
+
movement,
|
|
301
|
+
record,
|
|
302
|
+
resume_point,
|
|
303
|
+
standing_here,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export type { Movement, Standing }
|
|
307
|
+
export { review_reviewed_tree }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
2
|
+
import { review_finding_ledger, type DisposedLine } from './review-finding-ledger'
|
|
3
|
+
|
|
4
|
+
// Whether round 1's High/Medium findings all closed, read from the issue's own ledger file rather
|
|
5
|
+
// than asserted by the caller (joshuafolkken/kit#5286). Closed means every High/Medium `- rf:` line
|
|
6
|
+
// of the issue carries `fixed` or `false-positive`; a line with no disposition, a filed or deferred
|
|
7
|
+
// one, or no recorded round at all leaves round 1 open, so the answer stays `required`. Only the
|
|
8
|
+
// issue's latest code round is read (joshuafolkken/kit#5359): an earlier round's unfixed lines stay
|
|
9
|
+
// in the append-only ledger after a clean re-run.
|
|
10
|
+
|
|
11
|
+
const BLOCKING_SEVERITIES: ReadonlySet<string> = new Set(['high', 'medium'])
|
|
12
|
+
const NO_RECORD_REASON =
|
|
13
|
+
'no round-1 round is recorded in the ledger for this issue, so its findings cannot be read as closed'
|
|
14
|
+
|
|
15
|
+
const OPEN_REASON_PREFIX =
|
|
16
|
+
'round 1 left High/Medium findings neither fixed nor a false positive in the ledger'
|
|
17
|
+
const NO_DISPOSITION = 'no disposition'
|
|
18
|
+
|
|
19
|
+
interface Closure {
|
|
20
|
+
is_closed: boolean
|
|
21
|
+
reason: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const CLOSED: Closure = { is_closed: true, reason: '' }
|
|
25
|
+
|
|
26
|
+
function is_open(line: DisposedLine): boolean {
|
|
27
|
+
return (
|
|
28
|
+
BLOCKING_SEVERITIES.has(line.severity) && !review_finding_ledger.is_closing(line.disposition)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function open_reason(open: ReadonlyArray<DisposedLine>): string {
|
|
33
|
+
const dispositions = open.map((line) => line.disposition ?? NO_DISPOSITION).join(', ')
|
|
34
|
+
|
|
35
|
+
return `${OPEN_REASON_PREFIX} (${String(open.length)}: ${dispositions}), so the pass that verifies them is due`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function of_content(content: string | undefined, issue: number): Closure {
|
|
39
|
+
const latest = review_finding_ledger.latest_round(content ?? '', issue)
|
|
40
|
+
const lines = review_finding_ledger.issue_dispositions(latest, issue)
|
|
41
|
+
|
|
42
|
+
if (lines.length === 0) return { is_closed: false, reason: NO_RECORD_REASON }
|
|
43
|
+
|
|
44
|
+
const open = lines.filter((line) => is_open(line))
|
|
45
|
+
|
|
46
|
+
return open.length === 0 ? CLOSED : { is_closed: false, reason: open_reason(open) }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// `undefined` issue — a branch that names none — has no ledger lines to read, so it is open.
|
|
50
|
+
async function read(
|
|
51
|
+
issue: number | undefined,
|
|
52
|
+
root: string = observation_ledger_home.ledger_root(),
|
|
53
|
+
): Promise<Closure> {
|
|
54
|
+
if (issue === undefined) return { is_closed: false, reason: NO_RECORD_REASON }
|
|
55
|
+
|
|
56
|
+
return of_content(await observation_ledger_home.read(root), issue)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const review_round_one_closure = { NO_RECORD_REASON, OPEN_REASON_PREFIX, of_content, read }
|
|
60
|
+
|
|
61
|
+
export type { Closure }
|
|
62
|
+
export { review_round_one_closure }
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { issue_watch_gate } from '#scripts/issue/issue-watch-gate'
|
|
2
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
3
|
+
import { review_record } from './review-record'
|
|
4
|
+
import { review_reviewed_tree } from './review-reviewed-tree'
|
|
5
|
+
|
|
6
|
+
// Whether `josh ship` reuses the recorded round 1 rather than reviewing it again — asked by the ship
|
|
7
|
+
// and by `josh review:record --check`, so a lane resuming on an old record reads the ship's own answer
|
|
8
|
+
// rather than mistaking "recorded" for "reused" (joshuafolkken/kit#4763).
|
|
9
|
+
|
|
10
|
+
// The issue changed after the recorded review was briefed — the refusal `josh followup` would make
|
|
11
|
+
// (joshuafolkken/kit#4221). A read that fails reads as fresh: `josh followup` still refuses a stale
|
|
12
|
+
// review at the merge.
|
|
13
|
+
async function is_stale_here(): Promise<boolean> {
|
|
14
|
+
try {
|
|
15
|
+
return (await issue_watch_gate.review_refusal_here()) !== undefined
|
|
16
|
+
} catch (error) {
|
|
17
|
+
error_text.trace_swallowed('review_round_reuse.is_stale_here', error)
|
|
18
|
+
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// What the ship does with the recorded round 1: `held` reuses it with nothing left to read; `delta`
|
|
24
|
+
// reuses it and leaves what moved since to round 2; `again` reviews round 1 again.
|
|
25
|
+
const REUSE = { AGAIN: 'again', DELTA: 'delta', HELD: 'held' } as const
|
|
26
|
+
|
|
27
|
+
type Reuse = (typeof REUSE)[keyof typeof REUSE]
|
|
28
|
+
|
|
29
|
+
const { MOVEMENT } = review_reviewed_tree
|
|
30
|
+
|
|
31
|
+
// The reuse, and on `again` why (joshuafolkken/kit#4941): `review:record --check` prints it, so a lane
|
|
32
|
+
// reads which condition re-runs round 1 rather than a list of every condition that could.
|
|
33
|
+
interface ReuseAnswer {
|
|
34
|
+
reuse: Reuse
|
|
35
|
+
cause?: string | undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const STALE_CAUSE = "the issue changed after the recorded round's brief"
|
|
39
|
+
const UNRECORDED_CAUSE = 'no review round is recorded for this issue'
|
|
40
|
+
|
|
41
|
+
async function recorded_answer(issue: number): Promise<ReuseAnswer> {
|
|
42
|
+
const { movement, cause } = await review_reviewed_tree.standing_here()
|
|
43
|
+
|
|
44
|
+
if (movement === MOVEMENT.HELD) return { reuse: REUSE.HELD }
|
|
45
|
+
|
|
46
|
+
const is_delta = movement === MOVEMENT.DELTA || review_reviewed_tree.is_stopped_on(issue)
|
|
47
|
+
|
|
48
|
+
return is_delta ? { reuse: REUSE.DELTA } : { reuse: REUSE.AGAIN, cause }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// **A recorded round 1 is reused while the tree it was recorded on holds still** — a lane's review
|
|
52
|
+
// before the ship (joshuafolkken/kit#4620) — **or once it moved only where round 2 reads**
|
|
53
|
+
// (joshuafolkken/kit#4878): round 2 reads that delta from the round-1 brief's stamp after the commit, so
|
|
54
|
+
// round 1 run again would read it twice. **The ship's own round 1 that stopped on a finding** is reused
|
|
55
|
+
// however the lane's fix moved the tree, for the same reason. A High-cited file's fix is such a move
|
|
56
|
+
// where round 2's delta holds it (joshuafolkken/kit#4941). Any other move — a doc, a revert — is
|
|
57
|
+
// reviewed again, from that record (`review-reviewed-tree.ts`). An issue that changed after that
|
|
58
|
+
// round's brief is never reused: the recorded round never read it, and `josh followup` refuses the merge on it
|
|
59
|
+
// (joshuafolkken/kit#4221).
|
|
60
|
+
async function answer(issue: number, is_stale: boolean): Promise<ReuseAnswer> {
|
|
61
|
+
if (is_stale) return { reuse: REUSE.AGAIN, cause: STALE_CAUSE }
|
|
62
|
+
|
|
63
|
+
const recorded = await review_record.check(issue)
|
|
64
|
+
|
|
65
|
+
if (recorded.status !== 'ok') return { reuse: REUSE.AGAIN, cause: UNRECORDED_CAUSE }
|
|
66
|
+
|
|
67
|
+
return await recorded_answer(issue)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function reuse(issue: number, is_stale: boolean): Promise<Reuse> {
|
|
71
|
+
const reused = await answer(issue, is_stale)
|
|
72
|
+
|
|
73
|
+
return reused.reuse
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function is_reusable(issue: number, is_stale: boolean): Promise<boolean> {
|
|
77
|
+
return (await reuse(issue, is_stale)) !== REUSE.AGAIN
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The ship's own answer for this checkout, asked before the ship runs; a git failure throws.
|
|
81
|
+
async function answer_here(issue: number): Promise<ReuseAnswer> {
|
|
82
|
+
return await answer(issue, await is_stale_here())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function reuse_here(issue: number): Promise<Reuse> {
|
|
86
|
+
const reused = await answer_here(issue)
|
|
87
|
+
|
|
88
|
+
return reused.reuse
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function is_reusable_here(issue: number): Promise<boolean> {
|
|
92
|
+
return (await reuse_here(issue)) !== REUSE.AGAIN
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const review_round_reuse = {
|
|
96
|
+
REUSE,
|
|
97
|
+
answer,
|
|
98
|
+
answer_here,
|
|
99
|
+
is_reusable,
|
|
100
|
+
is_reusable_here,
|
|
101
|
+
is_stale_here,
|
|
102
|
+
reuse_here,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type { Reuse, ReuseAnswer }
|
|
106
|
+
export { review_round_reuse }
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { change_base } from '#scripts/git/change-base'
|
|
4
|
+
import { git_branch } from '#scripts/git/git-branch'
|
|
5
|
+
import { git_command } from '#scripts/git/git-command'
|
|
6
|
+
import { issue_number_shape } from '#scripts/issue/issue-number-shape'
|
|
4
7
|
import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
|
|
5
8
|
import { path_decision } from '#scripts/josh/path-decision'
|
|
6
9
|
import { review_code_tree } from './review-code-tree'
|
|
10
|
+
import { review_reviewed_tree } from './review-reviewed-tree'
|
|
7
11
|
import { review_round_one_blobs } from './review-round-one-blobs'
|
|
12
|
+
import { review_round_one_closure } from './review-round-one-closure'
|
|
8
13
|
import { review_round2, type RoundTwoAnswer } from './review-round2'
|
|
14
|
+
import { review_round2_skip_comment } from './review-round2-skip-comment'
|
|
9
15
|
import { review_stamps } from './review-stamps'
|
|
10
16
|
import { review_tree } from './review-tree'
|
|
11
17
|
|
|
@@ -18,42 +24,82 @@ import { review_tree } from './review-tree'
|
|
|
18
24
|
// `review-round2.ts`; everything here is the invocation.
|
|
19
25
|
|
|
20
26
|
const ARGV_OFFSET = 2
|
|
21
|
-
const USAGE = 'Usage: josh review:round2 [--
|
|
27
|
+
const USAGE = 'Usage: josh review:round2 [--record <N>] [--json]'
|
|
22
28
|
const FAILURE_EXIT_CODE = 1
|
|
23
29
|
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
+
// **Round 1's closure is read from the ledger, not asserted** (joshuafolkken/kit#5286): the flag that
|
|
31
|
+
// once stated it is refused with the one line that names what replaced it.
|
|
32
|
+
const RETIRED_CLOSED_FLAG = '--round-1-closed'
|
|
33
|
+
const RETIRED_CLOSED_LINE =
|
|
34
|
+
'--round-1-closed is retired: review:round2 reads round 1’s closure from the dispositions `review:record` keeps in the ledger — record each finding as `fixed <spec>` or `false-positive <spec>`, and pass --record <N> to name the issue.'
|
|
35
|
+
const RECORD_FLAG = '--record'
|
|
36
|
+
// The flag and its issue number.
|
|
37
|
+
const RECORD_ARGUMENT_COUNT = 2
|
|
38
|
+
const KNOWN_FLAGS: ReadonlyArray<string> = [path_decision.JSON_FLAG]
|
|
30
39
|
|
|
31
40
|
interface Options {
|
|
32
|
-
is_round_one_closed: boolean
|
|
33
41
|
is_json: boolean
|
|
42
|
+
// The issue `--record` names — whose ledger lines are read and where a skip is commented.
|
|
43
|
+
record: number | undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function without_record(argv: ReadonlyArray<string>): ReadonlyArray<string> | undefined {
|
|
47
|
+
const index = argv.indexOf(RECORD_FLAG)
|
|
48
|
+
|
|
49
|
+
if (index === -1) return argv
|
|
50
|
+
|
|
51
|
+
const value = argv[index + 1] ?? ''
|
|
52
|
+
|
|
53
|
+
return issue_number_shape.is_issue_number(value)
|
|
54
|
+
? argv.toSpliced(index, RECORD_ARGUMENT_COUNT)
|
|
55
|
+
: undefined
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
// `undefined` on anything unrecognized rather than a default, through the contract
|
|
37
|
-
// `path_decision.has_unknown_flag` states once for every mechanically-decided command: a
|
|
38
|
-
//
|
|
39
|
-
// is what makes the difference a matter of which flag was fumbled.
|
|
59
|
+
// `path_decision.has_unknown_flag` states once for every mechanically-decided command: a parser that
|
|
60
|
+
// guesses is what makes the answer a matter of which flag was fumbled.
|
|
40
61
|
function parse_options(argv: ReadonlyArray<string>): Options | undefined {
|
|
41
|
-
|
|
62
|
+
const rest = without_record(argv)
|
|
63
|
+
|
|
64
|
+
if (rest === undefined || path_decision.has_unknown_flag(rest, KNOWN_FLAGS)) return undefined
|
|
65
|
+
|
|
66
|
+
const index = argv.indexOf(RECORD_FLAG)
|
|
42
67
|
|
|
43
68
|
return {
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
is_json: rest.includes(path_decision.JSON_FLAG),
|
|
70
|
+
record: index === -1 ? undefined : Number(argv[index + 1]),
|
|
46
71
|
}
|
|
47
72
|
}
|
|
48
73
|
|
|
49
|
-
//
|
|
74
|
+
// The issue whose ledger lines decide round 1's closure: `--record`'s, else the branch's.
|
|
75
|
+
async function issue_of(options: Options): Promise<number | undefined> {
|
|
76
|
+
return options.record ?? git_branch.issue_from_branch(await git_command.branch())
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function report_record(issue: number, answer: RoundTwoAnswer): Promise<void> {
|
|
80
|
+
const recorded = await review_round2_skip_comment.record(issue, answer)
|
|
81
|
+
|
|
82
|
+
if (recorded === 'posted' || recorded === 'present') {
|
|
83
|
+
console.error(`${review_round2_skip_comment.HEADING}: ${recorded} on issue ${String(issue)}`)
|
|
84
|
+
} else if (recorded !== 'not-skipped') {
|
|
85
|
+
console.error(`${review_round2_skip_comment.HEADING}: not recorded (${recorded})`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Only the paths the code record covers are re-read, so a run without one parses nothing. **A file a
|
|
90
|
+
// recorded High cites is never comment-only** (joshuafolkken/kit#4941): `josh ship` reuses round 1 once
|
|
91
|
+
// that file's fix moved (`review-reviewed-tree.ts`), so a fix written in comments alone is still this
|
|
92
|
+
// round's to verify.
|
|
50
93
|
async function read_comment_only(base: string | undefined): Promise<ReadonlyArray<string>> {
|
|
51
94
|
const snapshot = review_stamps.round_one_code_stamp.read()
|
|
52
95
|
const paths = Object.keys(snapshot?.files ?? {})
|
|
53
96
|
const tree = await review_code_tree.read(paths)
|
|
54
97
|
const unmoved = review_round_one_blobs.unmoved({ from: snapshot?.base, to: base, paths })
|
|
98
|
+
const cited = new Set(review_reviewed_tree.high_cited())
|
|
55
99
|
|
|
56
|
-
return review_code_tree
|
|
100
|
+
return review_code_tree
|
|
101
|
+
.comment_only(snapshot, tree, base, unmoved)
|
|
102
|
+
.filter((path) => !cited.has(path))
|
|
57
103
|
}
|
|
58
104
|
|
|
59
105
|
// The carry over every path changed now, not only round 1's: a file the fix added after the merge is
|
|
@@ -82,34 +128,48 @@ function print(answer: RoundTwoAnswer, is_json: boolean): void {
|
|
|
82
128
|
path_decision.print_decision(review_round2.JSON_KEY, answer.verdict, answer.reason, is_json)
|
|
83
129
|
}
|
|
84
130
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (options === undefined) {
|
|
89
|
-
console.error(USAGE)
|
|
90
|
-
|
|
91
|
-
return FAILURE_EXIT_CODE
|
|
92
|
-
}
|
|
131
|
+
function refusal_of(argv: ReadonlyArray<string>): string {
|
|
132
|
+
return argv.includes(RETIRED_CLOSED_FLAG) ? RETIRED_CLOSED_LINE : USAGE
|
|
133
|
+
}
|
|
93
134
|
|
|
135
|
+
async function decide_here(options: Options): Promise<RoundTwoAnswer> {
|
|
136
|
+
const closure = await review_round_one_closure.read(await issue_of(options))
|
|
94
137
|
// The commit the tree below is a diff against, so the snapshot can be refused when it was taken
|
|
95
138
|
// against a different one — and the resolved commit rather than `git_command.change_base`, whose
|
|
96
|
-
// fallback is a ref name that moves and would compare equal to itself.
|
|
97
|
-
|
|
139
|
+
// fallback is a ref name that moves and would compare equal to itself. Read through a merge in
|
|
140
|
+
// progress, as the changed tree below is.
|
|
141
|
+
const base = await change_base.resolved_through_merge()
|
|
98
142
|
// No paths argument: `read_changed_tree` reads them itself, from the one definition of "changed"
|
|
99
143
|
// `josh review:level` and `josh review:brief` all decide from. Passing our own reading would be a
|
|
100
144
|
// second definition, which is the drift that module exists to prevent.
|
|
101
145
|
const tree = await review_tree.read_changed_tree()
|
|
102
|
-
|
|
146
|
+
|
|
147
|
+
return review_round2.decide({
|
|
103
148
|
base,
|
|
104
149
|
carried_delta: carried_reader(base, tree),
|
|
105
150
|
comment_only: await read_comment_only(base),
|
|
106
|
-
is_round_one_closed:
|
|
151
|
+
is_round_one_closed: closure.is_closed,
|
|
152
|
+
open_reason: closure.reason,
|
|
107
153
|
snapshot: review_stamps.round_one_stamp.read(),
|
|
108
154
|
tree,
|
|
109
155
|
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
159
|
+
const options = parse_options(argv)
|
|
160
|
+
|
|
161
|
+
if (options === undefined) {
|
|
162
|
+
console.error(refusal_of(argv))
|
|
163
|
+
|
|
164
|
+
return FAILURE_EXIT_CODE
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const decision = await decide_here(options)
|
|
110
168
|
|
|
111
169
|
print(decision, options.is_json)
|
|
112
170
|
|
|
171
|
+
if (options.record !== undefined) await report_record(options.record, decision)
|
|
172
|
+
|
|
113
173
|
return 0
|
|
114
174
|
}
|
|
115
175
|
|
|
@@ -118,11 +178,13 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
|
118
178
|
}
|
|
119
179
|
|
|
120
180
|
const review_round2_cli = {
|
|
121
|
-
CLOSED_FLAG,
|
|
122
181
|
carried_reader,
|
|
123
182
|
parse_options,
|
|
124
183
|
print,
|
|
125
184
|
read_comment_only,
|
|
185
|
+
RECORD_FLAG,
|
|
186
|
+
RETIRED_CLOSED_FLAG,
|
|
187
|
+
RETIRED_CLOSED_LINE,
|
|
126
188
|
run,
|
|
127
189
|
}
|
|
128
190
|
|