@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,383 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { setTimeout as sleep } from 'node:timers/promises'
|
|
3
|
+
import { core_budget } from '#scripts/gate/core-budget'
|
|
4
|
+
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
5
|
+
import { process_identity } from '#scripts/josh/process-identity'
|
|
6
|
+
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
7
|
+
import type { LaunchResult } from '#scripts/run/detached-launch'
|
|
8
|
+
import { test_pinned_ledger, type PinnedOrigin } from '#scripts/test/test-pinned-ledger'
|
|
9
|
+
import { test_pinned_record, type PinnedSummary } from '#scripts/test/test-pinned-record'
|
|
10
|
+
import { run_review_steps } from './run-review-steps'
|
|
11
|
+
|
|
12
|
+
// The background `josh test:pinned` of `josh run:review` (joshuafolkken/kit#4770): launched beside the
|
|
13
|
+
// gate and the brief, and waited on at `--join`. Run as its own `josh` command, it reserves its own
|
|
14
|
+
// cores (`DIRECT_UNIT_CLAIM`), so it shares the budget with the gate's units rather than overrunning it.
|
|
15
|
+
//
|
|
16
|
+
// **Two records, both keyed to the checkout.** The check writes its answer under the launch's token
|
|
17
|
+
// (`test-pinned-record.ts`); the start writes the launched pid and that token here, with the
|
|
18
|
+
// `--unpinned-ok` reason the join judges the answer by (joshuafolkken/kit#4793). No pid means no check
|
|
19
|
+
// was launched, and a pid that is gone without an answer is a check that died, which fails open the way
|
|
20
|
+
// a check that could not run does.
|
|
21
|
+
|
|
22
|
+
const PINNED_COMMAND = 'test:pinned'
|
|
23
|
+
const LAUNCH_PREFIX = 'josh-run-review-pinned-launch-'
|
|
24
|
+
const LOG_PREFIX = 'josh-run-review-pinned-log-'
|
|
25
|
+
const MS_PER_SECOND = 1000
|
|
26
|
+
const LOST_NOTE = 'its process ended without an answer'
|
|
27
|
+
const STOP_SIGNAL = 'SIGTERM'
|
|
28
|
+
|
|
29
|
+
// The join waits here only once the gate has settled and the review has returned, so the grace is
|
|
30
|
+
// counted from the end of the pair (joshuafolkken/kit#4794). A check still queued for its cores gets no
|
|
31
|
+
// grace at all (joshuafolkken/kit#4808): it could not finish within it. **Where a read follows, neither
|
|
32
|
+
// is stopped** (joshuafolkken/kit#4888): the check keeps running on the tree the join read, and the note
|
|
33
|
+
// names the one command that waits for its answer, so no lane runs `test:pinned` again by hand. Where
|
|
34
|
+
// none follows, the late check is stopped rather than left holding cores.
|
|
35
|
+
const GRACE_MS = 120_000
|
|
36
|
+
const READ_COMMAND = 'pnpm josh run:review --pinned'
|
|
37
|
+
const STILL_RUNNING_NOTE = `it is still running on this tree; wait for its answer with \`${READ_COMMAND}\` (a lane child: the ship's pre-detach reads it)`
|
|
38
|
+
const TIMED_OUT_NOTE = `no answer within ${String(GRACE_MS / MS_PER_SECOND)}s of the gate and the review`
|
|
39
|
+
const QUEUED_NOTE = 'it was still waiting for cores when the gate and the review ended'
|
|
40
|
+
// A round 1 with fixes to make moves the tree, and the round's `josh review:record` starts the check
|
|
41
|
+
// again on the fixed one — a lane child runs no `josh check:related` after the fixes
|
|
42
|
+
// (joshuafolkken/kit#4983) — so the join waits no grace on this tree's (joshuafolkken/kit#4895).
|
|
43
|
+
const FIXING_NOTE =
|
|
44
|
+
"round 1 has fixes to make — it is left running, and the round's `josh review:record` starts it again on the fixed tree for the ship's pre-detach to read"
|
|
45
|
+
// `josh run:review --pinned` reads a check `josh review:record` started seconds before
|
|
46
|
+
// (joshuafolkken/kit#4834), so it waits through the core queue and the whole run; the bound only
|
|
47
|
+
// stops a check that hung.
|
|
48
|
+
const ALONE_GRACE_MS = 900_000
|
|
49
|
+
const ALONE_TIMED_OUT_NOTE = `no answer within ${String(ALONE_GRACE_MS / MS_PER_SECOND)}s of the read`
|
|
50
|
+
|
|
51
|
+
type Clock = () => number
|
|
52
|
+
|
|
53
|
+
// `start` is the launched pid's start time, so a pid the OS has since reissued is told apart from the
|
|
54
|
+
// check (joshuafolkken/kit#4828). `tree` is the digest of the tree it checks (`run-review-pinned-tree.ts`),
|
|
55
|
+
// so `josh review:record` tells a round-1 fix that moved the tree (joshuafolkken/kit#4834).
|
|
56
|
+
// `launched_at` is the launch's epoch milliseconds, the start of a stopped check's wall time; `origin`
|
|
57
|
+
// its launcher, the one a stopped check's ledger row names (joshuafolkken/kit#5327).
|
|
58
|
+
interface Launch {
|
|
59
|
+
pid: number
|
|
60
|
+
token: string
|
|
61
|
+
reason?: string | undefined
|
|
62
|
+
start?: string | undefined
|
|
63
|
+
tree?: string | undefined
|
|
64
|
+
launched_at?: number | undefined
|
|
65
|
+
origin?: PinnedOrigin | undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function launch_path(root: string = PROJECT_ROOT): string {
|
|
69
|
+
return stamp_file.stamp_path(LAUNCH_PREFIX, root)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function log_path(root: string = PROJECT_ROOT): string {
|
|
73
|
+
return stamp_file.stamp_path(LOG_PREFIX, root, '.log')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface LaunchOptions {
|
|
77
|
+
root: string
|
|
78
|
+
token: string
|
|
79
|
+
argv: Array<string>
|
|
80
|
+
origin: PinnedOrigin
|
|
81
|
+
reason?: string | undefined
|
|
82
|
+
tree?: string | undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function launch_with({ root, token, argv, origin, reason, tree }: LaunchOptions): LaunchResult {
|
|
86
|
+
const launched = run_review_steps.launch_josh(argv, log_path(root), root)
|
|
87
|
+
|
|
88
|
+
if (launched.kind === 'launched') {
|
|
89
|
+
const { pid } = launched
|
|
90
|
+
|
|
91
|
+
stamp_file.write_stamp(launch_path(root), {
|
|
92
|
+
pid,
|
|
93
|
+
token,
|
|
94
|
+
reason,
|
|
95
|
+
start: process_identity.read_start(pid),
|
|
96
|
+
tree,
|
|
97
|
+
launched_at: Date.now(),
|
|
98
|
+
origin,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return launched
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The token ties the answer to this launch (`test-pinned-record.ts`).
|
|
106
|
+
function launch(root: string = PROJECT_ROOT, reason?: string, tree?: string): LaunchResult {
|
|
107
|
+
const token = randomUUID()
|
|
108
|
+
const origin = test_pinned_ledger.BACKGROUND_ORIGIN
|
|
109
|
+
const argv = test_pinned_record.launch_argv(token, reason, origin)
|
|
110
|
+
|
|
111
|
+
return launch_with({ root, token, argv, origin, reason, tree })
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// **A pre-detach that stopped at its first unpinned hunk goes on probing the rest here**
|
|
115
|
+
// (joshuafolkken/kit#5327): every hunk, with no reason, on the stamp `launch` writes, so the next
|
|
116
|
+
// pre-detach joins it (`run-review-pinned-join.ts`) and `clear` discards it as it does that one.
|
|
117
|
+
function launch_remaining(root: string = PROJECT_ROOT, tree?: string): LaunchResult {
|
|
118
|
+
const token = randomUUID()
|
|
119
|
+
const origin: PinnedOrigin = 'pre-detach'
|
|
120
|
+
const argv = test_pinned_record.launch_argv(token, undefined, origin, true)
|
|
121
|
+
|
|
122
|
+
return launch_with({ root, token, argv, origin, tree })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function optional_text(value: unknown): string | undefined {
|
|
126
|
+
return typeof value === 'string' ? value : undefined
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// A stamp written before `origin` was kept names none; its stop is written as `run:review`'s.
|
|
130
|
+
function origin_of(value: unknown): PinnedOrigin | undefined {
|
|
131
|
+
return typeof value === 'string' ? test_pinned_ledger.origin_of(value) : undefined
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function to_launch(value: unknown): Launch | undefined {
|
|
135
|
+
const { pid, token, reason, start, tree, launched_at, origin } = value as Record<string, unknown>
|
|
136
|
+
if (typeof pid !== 'number' || typeof token !== 'string') return undefined
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
pid,
|
|
140
|
+
token,
|
|
141
|
+
reason: optional_text(reason),
|
|
142
|
+
start: optional_text(start),
|
|
143
|
+
tree: optional_text(tree),
|
|
144
|
+
launched_at: typeof launched_at === 'number' ? launched_at : undefined,
|
|
145
|
+
origin: origin_of(origin),
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function read_launch(root: string): Launch | undefined {
|
|
150
|
+
const raw = stamp_file.read_stamp_text(launch_path(root))
|
|
151
|
+
|
|
152
|
+
if (raw === undefined) return undefined
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
return to_launch(JSON.parse(raw))
|
|
156
|
+
} catch {
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// One read: the answer, `undefined` while the check is still running, or a not-run answer once its
|
|
162
|
+
// process is gone without one. The record is read again after the pid is found gone, because the
|
|
163
|
+
// check writes its answer before it exits.
|
|
164
|
+
function read_answer(root: string, { pid, token, start }: Launch): PinnedSummary | undefined {
|
|
165
|
+
const summary = test_pinned_record.read(token, root)
|
|
166
|
+
|
|
167
|
+
if (summary !== undefined) return summary
|
|
168
|
+
if (process_identity.is_same_process(pid, start) !== false) return undefined
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
test_pinned_record.read(token, root) ?? test_pinned_record.not_run_summary(new Error(LOST_NOTE))
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// The launch is detached, so its pid leads a process group holding the `josh` process and its Vitest
|
|
176
|
+
// workers; signalling the group stops them all, and `base-tree.ts` removes the trees they held.
|
|
177
|
+
function signal_group(pid: number): void {
|
|
178
|
+
try {
|
|
179
|
+
process.kill(-pid, STOP_SIGNAL)
|
|
180
|
+
} catch {
|
|
181
|
+
// It ended between the check and the signal; there is nothing left to stop.
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// **A check stopped while it runs is a discarded probe** (joshuafolkken/kit#5262): it writes no ledger
|
|
186
|
+
// row of its own, so the stop writes one with the wall time it ran. A launch stamped before
|
|
187
|
+
// `launched_at` has no wall time, and writes none.
|
|
188
|
+
function discard(launched: Launch): void {
|
|
189
|
+
signal_group(launched.pid)
|
|
190
|
+
if (launched.launched_at === undefined) return
|
|
191
|
+
|
|
192
|
+
void test_pinned_ledger.record_discarded(Date.now() - launched.launched_at, launched.origin)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// A stale stamp can outlive its check by days, so the start signals only a pid whose start time
|
|
196
|
+
// matches the launch's: a reissued pid, or one whose start could not be read, may lead another
|
|
197
|
+
// lane's group.
|
|
198
|
+
function stop_last(launched: Launch): void {
|
|
199
|
+
if (process_identity.is_same_process(launched.pid, launched.start) === true) discard(launched)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// The stamp a waiter just read is this pair's, minutes old, so the lone read and a supervised join
|
|
203
|
+
// signal any pid not proven gone: one whose start could not be read (no `/proc`, no `ps`) is the check
|
|
204
|
+
// they have just been waiting on.
|
|
205
|
+
function stop_current(launched: Launch): void {
|
|
206
|
+
if (process_identity.is_same_process(launched.pid, launched.start) !== false) discard(launched)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Every start stops the last pair's check and clears its records first, so a join never reads an
|
|
210
|
+
// earlier tree's answer and a reopened pair leaves no check running unowned (joshuafolkken/kit#4828).
|
|
211
|
+
function clear(root: string = PROJECT_ROOT): void {
|
|
212
|
+
const last = read_launch(root)
|
|
213
|
+
|
|
214
|
+
if (last !== undefined) {
|
|
215
|
+
stop_last(last)
|
|
216
|
+
test_pinned_record.clear(last.token, root)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
stamp_file.remove_stamp(launch_path(root))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// The dispatcher's claim in the core ledger (`core-budget.ts`) carries `admitted_at` from the moment
|
|
223
|
+
// the check is granted its cores, before its script even boots, so a claim from this checkout not yet
|
|
224
|
+
// admitted is a check still queued. A check that claimed nothing (a parent held the cores) is not one.
|
|
225
|
+
function is_queued(root: string): boolean {
|
|
226
|
+
return core_budget
|
|
227
|
+
.live_reservations()
|
|
228
|
+
.some(
|
|
229
|
+
({ reservation }) =>
|
|
230
|
+
reservation.command === PINNED_COMMAND &&
|
|
231
|
+
reservation.checkout === root &&
|
|
232
|
+
reservation.admitted_at === undefined,
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// A check given up on fails open and is stopped rather than left burning the cores.
|
|
237
|
+
function stopped(launched: Launch, note: string): PinnedSummary {
|
|
238
|
+
stop_current(launched)
|
|
239
|
+
|
|
240
|
+
return test_pinned_record.not_run_summary(new Error(note))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function poll_within_grace(
|
|
244
|
+
root: string,
|
|
245
|
+
launched: Launch,
|
|
246
|
+
now: Clock,
|
|
247
|
+
grace_ms: number = GRACE_MS,
|
|
248
|
+
): Promise<PinnedSummary | undefined> {
|
|
249
|
+
const started = now()
|
|
250
|
+
let summary = read_answer(root, launched)
|
|
251
|
+
|
|
252
|
+
while (summary === undefined && now() - started < grace_ms) {
|
|
253
|
+
// eslint-disable-next-line no-await-in-loop -- polling: each read waits on the state the previous one saw
|
|
254
|
+
await sleep(run_review_steps.POLL_INTERVAL_MS)
|
|
255
|
+
summary = read_answer(root, launched)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return summary
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function given_up(launched: Launch, note: string, is_left_running: boolean): PinnedSummary {
|
|
262
|
+
if (!is_left_running) return stopped(launched, note)
|
|
263
|
+
|
|
264
|
+
return test_pinned_record.not_run_summary(new Error(`${note} — ${STILL_RUNNING_NOTE}`))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// The check usually settles while the review is still running; the grace only bounds the join's wait
|
|
268
|
+
// on one that fell far behind the pair while running its tests.
|
|
269
|
+
async function poll_answer(
|
|
270
|
+
root: string,
|
|
271
|
+
launched: Launch,
|
|
272
|
+
now: Clock,
|
|
273
|
+
is_left_running: boolean,
|
|
274
|
+
): Promise<PinnedSummary> {
|
|
275
|
+
const summary = read_answer(root, launched)
|
|
276
|
+
|
|
277
|
+
if (summary !== undefined) return summary
|
|
278
|
+
if (is_queued(root)) return given_up(launched, QUEUED_NOTE, is_left_running)
|
|
279
|
+
const late = await poll_within_grace(root, launched, now)
|
|
280
|
+
|
|
281
|
+
return late ?? given_up(launched, TIMED_OUT_NOTE, is_left_running)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// The `--unpinned-ok` reason the last check was launched with; `undefined` when it carried none.
|
|
285
|
+
function launched_reason(root: string = PROJECT_ROOT): string | undefined {
|
|
286
|
+
return read_launch(root)?.reason
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// A reason given after the check ended (joshuafolkken/kit#4920) goes on its launch record, so a later
|
|
290
|
+
// restart on a moved tree carries it as a launch's own reason does.
|
|
291
|
+
function carry_reason(reason: string, root: string = PROJECT_ROOT): void {
|
|
292
|
+
const launched = read_launch(root)
|
|
293
|
+
|
|
294
|
+
if (launched !== undefined) stamp_file.write_stamp(launch_path(root), { ...launched, reason })
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// The tree the last check was launched on; `undefined` when none was, or its tree could not be read.
|
|
298
|
+
function launched_tree(root: string = PROJECT_ROOT): string | undefined {
|
|
299
|
+
return read_launch(root)?.tree
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// `undefined` when no check was launched. `is_left_running` is false where nothing reads a late answer.
|
|
303
|
+
async function wait_for_answer(
|
|
304
|
+
root: string = PROJECT_ROOT,
|
|
305
|
+
now: Clock = () => Date.now(),
|
|
306
|
+
is_left_running = true,
|
|
307
|
+
): Promise<PinnedSummary | undefined> {
|
|
308
|
+
const launched = read_launch(root)
|
|
309
|
+
|
|
310
|
+
if (launched === undefined) return undefined
|
|
311
|
+
|
|
312
|
+
return await poll_answer(root, launched, now, is_left_running)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// The answer as it stands, waiting nothing and stopping nothing; `undefined` when no check was launched.
|
|
316
|
+
// A check still running answers with `pending_note`, why it was not waited on.
|
|
317
|
+
function answer_now(
|
|
318
|
+
root: string = PROJECT_ROOT,
|
|
319
|
+
pending_note: string = FIXING_NOTE,
|
|
320
|
+
): PinnedSummary | undefined {
|
|
321
|
+
const launched = read_launch(root)
|
|
322
|
+
|
|
323
|
+
if (launched === undefined) return undefined
|
|
324
|
+
|
|
325
|
+
return read_answer(root, launched) ?? test_pinned_record.not_run_summary(new Error(pending_note))
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Whether the last check left its answer, rather than ending without one or never being launched.
|
|
329
|
+
function is_answered(root: string = PROJECT_ROOT): boolean {
|
|
330
|
+
const launched = read_launch(root)
|
|
331
|
+
|
|
332
|
+
return launched !== undefined && test_pinned_record.read(launched.token, root) !== undefined
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Whether a check was launched and has no answer yet: still running, rather than done or lost.
|
|
336
|
+
function is_pending(root: string = PROJECT_ROOT): boolean {
|
|
337
|
+
const launched = read_launch(root)
|
|
338
|
+
|
|
339
|
+
return launched !== undefined && read_answer(root, launched) === undefined
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Whether the last check's process is proven to be the one launched, as `stop_last` asks before it
|
|
343
|
+
// signals: a stale stamp's reissued or unreadable pid is not waited on.
|
|
344
|
+
function is_live(root: string = PROJECT_ROOT): boolean {
|
|
345
|
+
const launched = read_launch(root)
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
launched !== undefined &&
|
|
349
|
+
process_identity.is_same_process(launched.pid, launched.start) === true
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// The lone read's wait: a queued check is waited on, not stopped. `undefined` when no check was launched.
|
|
354
|
+
async function wait_alone(
|
|
355
|
+
root: string = PROJECT_ROOT,
|
|
356
|
+
now: Clock = () => Date.now(),
|
|
357
|
+
): Promise<PinnedSummary | undefined> {
|
|
358
|
+
const launched = read_launch(root)
|
|
359
|
+
|
|
360
|
+
if (launched === undefined) return undefined
|
|
361
|
+
const summary = await poll_within_grace(root, launched, now, ALONE_GRACE_MS)
|
|
362
|
+
|
|
363
|
+
return summary ?? stopped(launched, ALONE_TIMED_OUT_NOTE)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const run_review_pinned_steps = {
|
|
367
|
+
FIXING_NOTE,
|
|
368
|
+
answer_now,
|
|
369
|
+
carry_reason,
|
|
370
|
+
clear,
|
|
371
|
+
is_answered,
|
|
372
|
+
is_live,
|
|
373
|
+
is_pending,
|
|
374
|
+
launch,
|
|
375
|
+
launch_remaining,
|
|
376
|
+
launched_reason,
|
|
377
|
+
launched_tree,
|
|
378
|
+
log_path,
|
|
379
|
+
wait_alone,
|
|
380
|
+
wait_for_answer,
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export { run_review_pinned_steps }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { gate_tree, type GateTree } from '#scripts/gate/gate-tree'
|
|
3
|
+
import { run_record_path } from '#scripts/josh/run-record-path'
|
|
4
|
+
|
|
5
|
+
// The tree a background `josh test:pinned` was launched on, as one digest (joshuafolkken/kit#4834):
|
|
6
|
+
// `josh run:review` writes it on the launch record, and `josh review:record` compares the fixed tree
|
|
7
|
+
// with it, so a round-1 fix that moved the tree starts the check again before the ship's pre-detach
|
|
8
|
+
// reads it. Read from the same changed-file map and base the gate stamps (`gate-tree.ts`).
|
|
9
|
+
//
|
|
10
|
+
// **A reading git could not answer is no key.** An empty map or an unresolved base is not a tree, and
|
|
11
|
+
// two of them would compare equal; the caller treats a missing key as "cannot tell" and starts nothing.
|
|
12
|
+
//
|
|
13
|
+
// **A run's own record is not the tree** (joshuafolkken/kit#5002): an observation line or a `josh
|
|
14
|
+
// metrics` approval written after the launch is read by no test, so it leaves the key — and the
|
|
15
|
+
// launch's answer — as it was (`run-record-path.ts`, the rule the verdict cache reads too).
|
|
16
|
+
|
|
17
|
+
function digest_of({ files, base }: GateTree): string | undefined {
|
|
18
|
+
const entries = Object.entries(files)
|
|
19
|
+
|
|
20
|
+
if (base === undefined || entries.length === 0) return undefined
|
|
21
|
+
|
|
22
|
+
const code = entries
|
|
23
|
+
.filter(([file]) => !run_record_path.is_run_record(file))
|
|
24
|
+
.toSorted(([left], [right]) => left.localeCompare(right))
|
|
25
|
+
|
|
26
|
+
return createHash('sha256')
|
|
27
|
+
.update(JSON.stringify([base, code]))
|
|
28
|
+
.digest('hex')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function key(): Promise<string | undefined> {
|
|
32
|
+
return digest_of(await gate_tree.read_gate_tree())
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const run_review_pinned_tree = { digest_of, key }
|
|
36
|
+
|
|
37
|
+
export { run_review_pinned_tree }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { test_pinned_logic } from '#scripts/test/test-pinned-logic'
|
|
2
|
+
import type { PinnedSummary } from '#scripts/test/test-pinned-record'
|
|
3
|
+
|
|
4
|
+
// The round-1 check of `josh run:review` (joshuafolkken/kit#4647): `josh test:pinned` over the change,
|
|
5
|
+
// changed hunks and added files' functions alike. A change no test pins is the finding a review used
|
|
6
|
+
// to stop a lane on in round 2, so the run stops on it instead, with each place listed as `file:line`.
|
|
7
|
+
// It runs in the background beside the gate and the brief, and `--join` stops on its answer
|
|
8
|
+
// (joshuafolkken/kit#4770), so its time leaves the critical path.
|
|
9
|
+
//
|
|
10
|
+
// **`--unpinned-ok "<reason>"` carries on past it** for a change no test can pin. That check runs in
|
|
11
|
+
// the background too (joshuafolkken/kit#4793), so the pair never waits on it: the join prints the list
|
|
12
|
+
// and the reason and carries on. **Given after the join stopped, on the same tree and a green gate,
|
|
13
|
+
// the reason judges the finished answer** and re-runs neither the check nor the pair
|
|
14
|
+
// (joshuafolkken/kit#4920); an answer cut at its first unpinned hunk re-runs only the whole check
|
|
15
|
+
// (joshuafolkken/kit#5153). Unchecked places (over the cap, an added file `typescript` could not
|
|
16
|
+
// read, or no Vitest to run a probe) never stop the run.
|
|
17
|
+
|
|
18
|
+
const UNPINNED_OK_FLAG = '--unpinned-ok'
|
|
19
|
+
const OK_ARGUMENT_COUNT = 2
|
|
20
|
+
const STOP_NOTE =
|
|
21
|
+
'Pin each unpinned place with a test that turns red without it, then re-run `pnpm josh run:review`; for a change no test can pin, leave the tree as it is and pass the reason with `pnpm josh run:review --unpinned-ok "<reason>"` — on this tree and a green gate it judges this answer (or, cut at its first unpinned place, checks the whole change) and re-runs neither the gate nor the pair (`pnpm josh ship --review` forwards it).'
|
|
22
|
+
|
|
23
|
+
type PinnedKind = 'clear' | 'stop' | 'accepted'
|
|
24
|
+
|
|
25
|
+
interface PinnedVerdict {
|
|
26
|
+
kind: PinnedKind
|
|
27
|
+
text: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// `--unpinned-ok` with a non-blank reason; anything else carries none.
|
|
31
|
+
function ok_reason_of(argv: ReadonlyArray<string>): string | undefined {
|
|
32
|
+
const [flag, reason = ''] = argv
|
|
33
|
+
if (flag !== UNPINNED_OK_FLAG || argv.length !== OK_ARGUMENT_COUNT) return undefined
|
|
34
|
+
|
|
35
|
+
return reason.trim() === '' ? undefined : reason
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// The join's answer on the background check, judged by the reason it was launched with; its note goes
|
|
39
|
+
// above the counts line (`test_pinned_logic.report_text`), so a `tail` keeps what to do.
|
|
40
|
+
function verdict_of(summary: PinnedSummary, reason: string | undefined): PinnedVerdict {
|
|
41
|
+
const { report_text } = test_pinned_logic
|
|
42
|
+
|
|
43
|
+
if (summary.unpinned.length === 0) return { kind: 'clear', text: report_text(summary) }
|
|
44
|
+
if (reason === undefined) return { kind: 'stop', text: report_text(summary, `${STOP_NOTE}\n`) }
|
|
45
|
+
const note = `Unpinned accepted by the implementer: ${reason}\n`
|
|
46
|
+
|
|
47
|
+
return { kind: 'accepted', text: report_text(summary, note) }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const run_review_pinned = { STOP_NOTE, UNPINNED_OK_FLAG, ok_reason_of, verdict_of }
|
|
51
|
+
|
|
52
|
+
export type { PinnedVerdict }
|
|
53
|
+
export { run_review_pinned }
|
|
@@ -3,7 +3,7 @@ import { gate_skip } from '#scripts/gate/gate-skip'
|
|
|
3
3
|
import { gate_tree } from '#scripts/gate/gate-tree'
|
|
4
4
|
import { git_location_environment } from '#scripts/git/git-location-environment'
|
|
5
5
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
6
|
-
import { file_map_stamp } from '#scripts/josh/file-map-stamp'
|
|
6
|
+
import { file_map_stamp, type FileMapStampAccess } from '#scripts/josh/file-map-stamp'
|
|
7
7
|
import { GATE_COMMAND } from '#scripts/josh/josh-command-types'
|
|
8
8
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
9
9
|
import { review_stamps } from '#scripts/review/review-stamps'
|
|
@@ -12,9 +12,10 @@ import { detached_launch, type LaunchArgv, type LaunchResult } from '#scripts/ru
|
|
|
12
12
|
import { run_review, type GateState, type ReviewTiming, type TimingStart } from './run-review'
|
|
13
13
|
|
|
14
14
|
// The side effects `josh run:review` performs, kept out of `run-review.ts` the way `run-merge-steps.ts`
|
|
15
|
-
// is kept out of `run-merge.ts`. Three of them
|
|
16
|
-
//
|
|
17
|
-
// stamps a real `josh gate` writes, and
|
|
15
|
+
// is kept out of `run-merge.ts`. Three of them (the `test:pinned` check's are in
|
|
16
|
+
// `run-review-pinned-steps.ts`): launch the gate detached so one command leaves it running in the
|
|
17
|
+
// background, read back the state the join waits on from the stamps a real `josh gate` writes, and
|
|
18
|
+
// keep the four timestamps the overlap is measured from.
|
|
18
19
|
|
|
19
20
|
// Resolve `josh` through the target checkout's package script. Consumer lanes have no source entry.
|
|
20
21
|
const PNPM = 'pnpm'
|
|
@@ -22,6 +23,10 @@ const DIRECTORY_FLAG = '--dir'
|
|
|
22
23
|
const JOSH_SCRIPT = 'josh'
|
|
23
24
|
const GATE_LOG_PREFIX = 'josh-run-review-gate-log-'
|
|
24
25
|
const TIMING_PREFIX = 'josh-run-review-timing-'
|
|
26
|
+
const START_TREE_PREFIX = 'josh-run-review-start-tree-'
|
|
27
|
+
|
|
28
|
+
// The tree this pair's gate started on, which tells a moved tree from a red gate at the join.
|
|
29
|
+
const start_tree_stamp: FileMapStampAccess = file_map_stamp.create(START_TREE_PREFIX, PROJECT_ROOT)
|
|
25
30
|
|
|
26
31
|
// The gate is a local, no-network command that finishes in tens of seconds; the review it runs beside
|
|
27
32
|
// is the longer pole, so by the time the child joins, the gate has almost always already settled. The
|
|
@@ -42,25 +47,70 @@ function timing_path(target: string = PROJECT_ROOT): string {
|
|
|
42
47
|
// The argv is composed from constants and the validated checkout root, never interpolated into a
|
|
43
48
|
// shell string — `detached_launch.launch` refuses one carrying anything a command line has no business
|
|
44
49
|
// holding, and there is no shell on the path to escape from.
|
|
45
|
-
function
|
|
50
|
+
function josh_argv(root: string, josh_args: ReadonlyArray<string>): LaunchArgv {
|
|
46
51
|
return {
|
|
47
52
|
command: PNPM,
|
|
48
|
-
args: [DIRECTORY_FLAG, root, JOSH_SCRIPT,
|
|
53
|
+
args: [DIRECTORY_FLAG, root, JOSH_SCRIPT, ...josh_args],
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
// A `josh` command left running in the background, its output kept at `log_path`: the gate, and the
|
|
58
|
+
// `test:pinned` check beside it (`run-review-pinned-steps.ts`).
|
|
59
|
+
function launch_josh(
|
|
60
|
+
josh_args: ReadonlyArray<string>,
|
|
61
|
+
log_path: string,
|
|
62
|
+
root: string = PROJECT_ROOT,
|
|
63
|
+
): LaunchResult {
|
|
53
64
|
return detached_launch.launch(
|
|
54
65
|
{
|
|
55
|
-
argv:
|
|
66
|
+
argv: josh_argv(root, josh_args),
|
|
56
67
|
cwd: root,
|
|
57
|
-
log_path
|
|
68
|
+
log_path,
|
|
58
69
|
env: git_location_environment.location_free_environment(),
|
|
59
70
|
},
|
|
60
71
|
(note) => process.stderr.write(`${note}\n`),
|
|
61
72
|
)
|
|
62
73
|
}
|
|
63
74
|
|
|
75
|
+
function launch_gate(root: string = PROJECT_ROOT): LaunchResult {
|
|
76
|
+
return launch_josh([GATE_COMMAND], gate_log_path(root), root)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function to_timing(value: unknown): TimingStart | undefined {
|
|
80
|
+
if (typeof value !== 'object' || value === null) return undefined
|
|
81
|
+
|
|
82
|
+
const { gate_started_at, review_started_at } = value as Record<string, unknown>
|
|
83
|
+
|
|
84
|
+
if (typeof gate_started_at !== 'string' || typeof review_started_at !== 'string') return undefined
|
|
85
|
+
|
|
86
|
+
return { gate_started_at, review_started_at }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function read_timing_start(target: string = PROJECT_ROOT): TimingStart | undefined {
|
|
90
|
+
const raw = stamp_file.read_stamp_text(timing_path(target))
|
|
91
|
+
|
|
92
|
+
if (raw === undefined) return undefined
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
return to_timing(JSON.parse(raw))
|
|
96
|
+
} catch {
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// **This pair's gate was green on the tree it started on** (joshuafolkken/kit#4774): a green record
|
|
102
|
+
// covering that tree is one the gate either wrote or reused, and a red gate leaves only a record it
|
|
103
|
+
// could not reuse — so none covers it. Read beside a record that no longer covers the current tree, it
|
|
104
|
+
// is the tree that moved after the gate passed — a round-1 reviewer's fix in place — and not a red
|
|
105
|
+
// gate. The record's time is no witness: a gate that reused an earlier green writes no new one.
|
|
106
|
+
function is_green_on_start_tree(base: string | undefined): boolean {
|
|
107
|
+
const start = start_tree_stamp.read()
|
|
108
|
+
|
|
109
|
+
if (start === undefined) return false
|
|
110
|
+
|
|
111
|
+
return gate_skip.reusable_green_gate(start.files, base) !== undefined
|
|
112
|
+
}
|
|
113
|
+
|
|
64
114
|
// Green wins over the marker: a gate that has recorded a green result on this tree is done however its
|
|
65
115
|
// in-flight marker reads. `reusable_green_gate` is the gate's own reuse test rather than a second copy
|
|
66
116
|
// of it, so the join calls a gate green on exactly the trees the gate would skip.
|
|
@@ -74,7 +124,7 @@ async function read_gate_state(): Promise<GateState> {
|
|
|
74
124
|
const tree = await gate_tree.read_gate_tree()
|
|
75
125
|
const is_green = gate_skip.reusable_green_gate(tree.files, tree.base) !== undefined
|
|
76
126
|
|
|
77
|
-
return run_review.gate_state(is_green, is_running)
|
|
127
|
+
return run_review.gate_state(is_green, is_running, is_green_on_start_tree(tree.base))
|
|
78
128
|
}
|
|
79
129
|
|
|
80
130
|
// The brief has to report the gate as in-flight, or it says `Not verified` and sends the review agent
|
|
@@ -82,9 +132,13 @@ async function read_gate_state(): Promise<GateState> {
|
|
|
82
132
|
// that marker itself, but its cold start can outrun the brief — so the composite writes the marker up
|
|
83
133
|
// front, for the tree it is about to review. The gate overwrites it with its own identity the moment it
|
|
84
134
|
// starts and clears it when it finishes, so this only closes the window between launch and that write;
|
|
85
|
-
// the marker carries this process's identity, which is live while the in-process brief reads it.
|
|
135
|
+
// the marker carries this process's identity, which is live while the in-process brief reads it. The
|
|
136
|
+
// same tree is kept as the pair's start tree, which the gate clears no marker of.
|
|
86
137
|
async function mark_gate_starting(): Promise<void> {
|
|
87
|
-
|
|
138
|
+
const tree = await review_tree.read_changed_tree()
|
|
139
|
+
|
|
140
|
+
review_stamps.in_flight_stamp.write(tree)
|
|
141
|
+
start_tree_stamp.write(tree)
|
|
88
142
|
}
|
|
89
143
|
|
|
90
144
|
// Waited on gate finish, for the join. The review is the longer pole, so this usually returns on its
|
|
@@ -111,28 +165,6 @@ function write_timing_start(start: TimingStart, target: string = PROJECT_ROOT):
|
|
|
111
165
|
stamp_file.write_stamp(timing_path(target), start)
|
|
112
166
|
}
|
|
113
167
|
|
|
114
|
-
function to_timing(value: unknown): TimingStart | undefined {
|
|
115
|
-
if (typeof value !== 'object' || value === null) return undefined
|
|
116
|
-
|
|
117
|
-
const { gate_started_at, review_started_at } = value as Record<string, unknown>
|
|
118
|
-
|
|
119
|
-
if (typeof gate_started_at !== 'string' || typeof review_started_at !== 'string') return undefined
|
|
120
|
-
|
|
121
|
-
return { gate_started_at, review_started_at }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function read_timing_start(target: string = PROJECT_ROOT): TimingStart | undefined {
|
|
125
|
-
const raw = stamp_file.read_stamp_text(timing_path(target))
|
|
126
|
-
|
|
127
|
-
if (raw === undefined) return undefined
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
return to_timing(JSON.parse(raw))
|
|
131
|
-
} catch {
|
|
132
|
-
return undefined
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
168
|
// The two ends are stamped at the join: the review ended when the child returned from the subagent and
|
|
137
169
|
// called this, and the gate ended at the green stamp's time where it passed, or now where it did not.
|
|
138
170
|
function complete_timing(
|
|
@@ -147,13 +179,17 @@ function complete_timing(
|
|
|
147
179
|
}
|
|
148
180
|
|
|
149
181
|
const run_review_steps = {
|
|
182
|
+
MAX_JOIN_WAIT_MS,
|
|
183
|
+
POLL_INTERVAL_MS,
|
|
150
184
|
complete_timing,
|
|
151
185
|
gate_log_path,
|
|
152
186
|
launch_gate,
|
|
187
|
+
launch_josh,
|
|
153
188
|
mark_gate_starting,
|
|
154
189
|
read_gate_log,
|
|
155
190
|
read_gate_state,
|
|
156
191
|
read_timing_start,
|
|
192
|
+
start_tree_stamp,
|
|
157
193
|
timing_path,
|
|
158
194
|
wait_for_gate_finish,
|
|
159
195
|
write_timing_start,
|