@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto'
|
|
2
2
|
import type { AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
3
3
|
import { lane_close } from '#scripts/lane/lane-close'
|
|
4
|
+
import { lane_withdraw_closed } from '#scripts/lane/lane-withdraw-closed'
|
|
4
5
|
import { poll } from '#scripts/lib/poll'
|
|
5
6
|
import { run_carry } from '#scripts/run/carry/run-carry'
|
|
6
7
|
import { run_event_setup } from '#scripts/run/event/run-event-setup'
|
|
@@ -54,6 +55,15 @@ function wake_session(
|
|
|
54
55
|
return result
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
// The lanes the supervisor ends each pass: closes left incomplete, and lanes whose issue was closed as
|
|
59
|
+
// not planned (`lane-withdraw-closed.ts`), each withdrawal said on stderr.
|
|
60
|
+
async function retry_closes(): Promise<void> {
|
|
61
|
+
await lane_close.retry_closes()
|
|
62
|
+
const notes = await lane_withdraw_closed.withdraw_closed()
|
|
63
|
+
|
|
64
|
+
for (const note of notes) note_to_stderr(note)
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
// **A record this supervisor owns itself is not a session at work.** A failed driver leaves the record
|
|
58
68
|
// adopted for this process, so reading that owner as live would have every restarted pass wait on
|
|
59
69
|
// itself and never drive again.
|
|
@@ -79,9 +89,7 @@ function ports_for(
|
|
|
79
89
|
wake_session(context, invocation, profile, { id: session_id, material }),
|
|
80
90
|
sleep: poll.sleep,
|
|
81
91
|
now: () => new Date(),
|
|
82
|
-
retry_closes
|
|
83
|
-
await lane_close.retry_closes()
|
|
84
|
-
},
|
|
92
|
+
retry_closes,
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
|
|
@@ -9,6 +9,7 @@ import { claude_agent_argv } from '#scripts/agent/claude-agent-argv'
|
|
|
9
9
|
import { backlogrun_launch } from '#scripts/backlog/backlogrun-launch'
|
|
10
10
|
import { detached_launch, type LaunchArgv } from '#scripts/run/detached-launch'
|
|
11
11
|
import { run_invocation } from '#scripts/run/run-invocation'
|
|
12
|
+
import { run_wake_caffeinate } from './run-wake-caffeinate'
|
|
12
13
|
|
|
13
14
|
// How the supervisor starts things: the next agent session, and — at `--start` — its own detached
|
|
14
15
|
// self. Both go through one `launch`, because the two differ only in what is being run.
|
|
@@ -164,6 +165,7 @@ const run_wake_session = {
|
|
|
164
165
|
WAKE_FLAGS,
|
|
165
166
|
ensure_log: detached_launch.ensure_log,
|
|
166
167
|
is_safe_argv: detached_launch.is_safe_argv,
|
|
168
|
+
keep_awake: run_wake_caffeinate.keep_awake,
|
|
167
169
|
launch: detached_launch.launch,
|
|
168
170
|
chosen_profile,
|
|
169
171
|
scheduler_profile,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { agent_role_profile, type AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
2
2
|
import { backlog_budget } from '#scripts/backlog/backlog-budget'
|
|
3
|
+
import { durable_state } from '#scripts/josh/durable-state'
|
|
3
4
|
import { process_identity } from '#scripts/josh/process-identity'
|
|
4
5
|
import { process_owner_schema } from '#scripts/josh/process-owner'
|
|
5
6
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
@@ -16,7 +17,7 @@ import { z } from 'zod'
|
|
|
16
17
|
|
|
17
18
|
const WAKE_PREFIX = 'josh-run-wake-'
|
|
18
19
|
// The output of every session this starts, appended per repository so a silent death is diagnosable.
|
|
19
|
-
const WAKE_LOG_PREFIX =
|
|
20
|
+
const WAKE_LOG_PREFIX = durable_state.PREFIX.WAKE_LOG
|
|
20
21
|
const WAKE_LOG_SUFFIX = '.log'
|
|
21
22
|
// How long a woken session has to claim the carry record — the only launch-failure detector, since a
|
|
22
23
|
// claim also catches a session that dies in boot or never picks the run up. It covers a slow cold start.
|
|
@@ -153,7 +154,7 @@ function wake_path(git_directory: string): string {
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
function wake_log_path(git_directory: string): string {
|
|
156
|
-
return
|
|
157
|
+
return durable_state.path(WAKE_LOG_PREFIX, git_directory, WAKE_LOG_SUFFIX)
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
function parse_wake(raw: string): RunWake | undefined {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
|
-
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
3
5
|
import { composite_actions } from '#scripts/ci/composite-actions'
|
|
4
6
|
import { execaSync } from 'execa'
|
|
5
7
|
|
|
@@ -16,6 +18,9 @@ const VERSION_RE = /^(?<key>[ \t]*SAFE_CHAIN_INSTALLER_VERSION:[ \t]*)(?<value>\
|
|
|
16
18
|
const SHA256_RE = /^(?<key>[ \t]*SAFE_CHAIN_INSTALLER_SHA256:[ \t]*)(?<value>\S+)[ \t]*$/mu
|
|
17
19
|
const VALUE_GROUP = 'value'
|
|
18
20
|
const DOWNLOAD_TIMEOUT_MS = 30_000
|
|
21
|
+
const REPOSITORY = 'AikidoSec/safe-chain'
|
|
22
|
+
const INSTALLER_NAME = 'install-safe-chain.sh'
|
|
23
|
+
const ATTESTATION_FAILURE = 'could not be verified against its release attestation'
|
|
19
24
|
|
|
20
25
|
interface InstallerPin {
|
|
21
26
|
version: string
|
|
@@ -23,7 +28,7 @@ interface InstallerPin {
|
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
function installer_url(version: string): string {
|
|
26
|
-
return `https://github.com/
|
|
31
|
+
return `https://github.com/${REPOSITORY}/releases/download/${version}/${INSTALLER_NAME}`
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
// The local composite actions follow the workflows: kit's own pin lives in
|
|
@@ -50,7 +55,7 @@ function rewrite_pin(content: string, pin: InstallerPin): string {
|
|
|
50
55
|
.replace(SHA256_RE, (_match, key: string) => `${key}${pin.sha256}`)
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
function
|
|
58
|
+
function fetch_installer(version: string): Uint8Array | undefined {
|
|
54
59
|
// `--proto =https` keeps the release redirect from being followed onto plain HTTP.
|
|
55
60
|
const result = execaSync('curl', ['--proto', '=https', '-fsSL', installer_url(version)], {
|
|
56
61
|
encoding: 'buffer',
|
|
@@ -61,7 +66,41 @@ function fetch_installer_sha256(version: string): string | undefined {
|
|
|
61
66
|
})
|
|
62
67
|
if (result.exitCode !== 0 || result.stdout.length === 0) return undefined
|
|
63
68
|
|
|
64
|
-
return
|
|
69
|
+
return result.stdout
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// joshuafolkken/kit#4669: the hash recorded is the one the release's GitHub attestation signs, not
|
|
73
|
+
// whatever the download happened to return. `gh release verify-asset` checks the signed attestation
|
|
74
|
+
// GitHub issued when the release was published, so a release asset swapped afterwards fails here.
|
|
75
|
+
function verify_installer(version: string, installer: Uint8Array): boolean {
|
|
76
|
+
const directory = mkdtempSync(path.join(tmpdir(), 'safe-chain-installer-'))
|
|
77
|
+
const installer_path = path.join(directory, INSTALLER_NAME)
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
writeFileSync(installer_path, installer)
|
|
81
|
+
const result = execaSync(
|
|
82
|
+
'gh',
|
|
83
|
+
['release', 'verify-asset', version, installer_path, '--repo', REPOSITORY],
|
|
84
|
+
{
|
|
85
|
+
reject: false,
|
|
86
|
+
timeout: DOWNLOAD_TIMEOUT_MS,
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return result.exitCode === 0
|
|
91
|
+
} finally {
|
|
92
|
+
rmSync(directory, { recursive: true, force: true })
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The hash to record, or why there is none — a pin is written only from a verified installer.
|
|
97
|
+
function fetch_installer_sha256(version: string): { sha256: string } | { failure: string } {
|
|
98
|
+
const installer = fetch_installer(version)
|
|
99
|
+
if (installer === undefined) return { failure: 'could not be downloaded' }
|
|
100
|
+
// A missing or signed-out `gh` lands here too: no verification means no pin, whatever the reason.
|
|
101
|
+
if (!verify_installer(version, installer)) return { failure: ATTESTATION_FAILURE }
|
|
102
|
+
|
|
103
|
+
return { sha256: createHash('sha256').update(installer).digest('hex') }
|
|
65
104
|
}
|
|
66
105
|
|
|
67
106
|
// The workflows whose pin lags `latest` — a missing file or one without the pin is not a target.
|
|
@@ -83,15 +122,17 @@ function sync(latest: string, workflow_paths: ReadonlyArray<string> = list_workf
|
|
|
83
122
|
const targets = stale_workflows(latest, workflow_paths)
|
|
84
123
|
if (targets.length === 0) return
|
|
85
124
|
|
|
86
|
-
const
|
|
125
|
+
const fetched = fetch_installer_sha256(latest)
|
|
87
126
|
|
|
88
|
-
if (
|
|
89
|
-
console.warn(`\n⚠
|
|
127
|
+
if ('failure' in fetched) {
|
|
128
|
+
console.warn(`\n⚠ The safe-chain ${latest} installer ${fetched.failure}; CI pin left as is`)
|
|
90
129
|
|
|
91
130
|
return
|
|
92
131
|
}
|
|
93
132
|
|
|
94
|
-
for (const workflow_path of targets)
|
|
133
|
+
for (const workflow_path of targets) {
|
|
134
|
+
write_pin(workflow_path, { version: latest, sha256: fetched.sha256 })
|
|
135
|
+
}
|
|
95
136
|
}
|
|
96
137
|
|
|
97
138
|
const ci_installer_pin = {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { existsSync, readFileSync
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { file_reader } from '#scripts/lib/read-file'
|
|
4
4
|
import { yaml_document } from '#scripts/lib/yaml-document'
|
|
5
|
+
import { file_content } from '#scripts/sync/file-content'
|
|
5
6
|
import { release_window } from '#scripts/version/release-window'
|
|
6
7
|
import { dump } from 'js-yaml'
|
|
7
8
|
|
|
@@ -139,7 +140,7 @@ function sync_project_config(root: string): boolean {
|
|
|
139
140
|
const workspace = readFileSync(workspace_path, 'utf8')
|
|
140
141
|
const merged = merge_project_config(existing, workspace)
|
|
141
142
|
if (merged === existing) return false
|
|
142
|
-
|
|
143
|
+
file_content.write_contained(config_path, merged)
|
|
143
144
|
|
|
144
145
|
return true
|
|
145
146
|
}
|
|
@@ -7,7 +7,11 @@ import { execaSync } from 'execa'
|
|
|
7
7
|
|
|
8
8
|
const SECRETLINT_BIN = 'secretlint'
|
|
9
9
|
const NO_GLOB_FLAG = '--no-glob'
|
|
10
|
+
const END_OF_OPTIONS = '--'
|
|
10
11
|
const ARGV_OFFSET = 2
|
|
12
|
+
// Set by the CI `Secret Scan` job, where nothing provisions the binary later: there a missing
|
|
13
|
+
// secretlint would turn the scan into a silent pass, so it fails instead (kit#4657).
|
|
14
|
+
const REQUIRE_ENV = 'JOSH_SECRETLINT_REQUIRE'
|
|
11
15
|
|
|
12
16
|
// The scan is defense in depth ahead of GitHub push protection, not the only gate, so a
|
|
13
17
|
// missing binary must not block the commit. kit ships the pre-commit rule inside
|
|
@@ -21,7 +25,14 @@ const MISSING_NOTICE =
|
|
|
21
25
|
` The kit pre-commit hook ships ahead of the dependency it needs.\n` +
|
|
22
26
|
` Run \`pnpm josh sync && pnpm install\` to provision it.`
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
const REQUIRED_MISSING_ERROR =
|
|
29
|
+
`❌ secretlint is not installed, and ${REQUIRE_ENV} requires the scan.\n` +
|
|
30
|
+
` Add it to the devDependencies with \`pnpm josh sync && pnpm install\`.`
|
|
31
|
+
|
|
32
|
+
type ScanDecision =
|
|
33
|
+
| { kind: 'skip'; notice?: string }
|
|
34
|
+
| { kind: 'fail'; error: string }
|
|
35
|
+
| { kind: 'scan'; binary: string }
|
|
25
36
|
|
|
26
37
|
function resolve_secretlint_binary(project_root: string): string | undefined {
|
|
27
38
|
const candidate = resolve_local_bin(project_root, SECRETLINT_BIN)
|
|
@@ -31,17 +42,28 @@ function resolve_secretlint_binary(project_root: string): string | undefined {
|
|
|
31
42
|
|
|
32
43
|
// lefthook substitutes literal paths, so `--no-glob` is mandatory: a SvelteKit route directory
|
|
33
44
|
// such as `(app)` or `[id]` would otherwise reach secretlint's glob engine as a pattern.
|
|
34
|
-
// Masking and the .gitignore cascade are both on by default in v13 — no flag adds them.
|
|
45
|
+
// Masking and the .gitignore cascade are both on by default in v13 — no flag adds them. `--` ends
|
|
46
|
+
// the options, so a staged file named like `--secretlintrc=evil.json` stays a path, not a flag.
|
|
35
47
|
function build_scan_arguments(staged_files: ReadonlyArray<string>): Array<string> {
|
|
36
|
-
return [NO_GLOB_FLAG, ...staged_files]
|
|
48
|
+
return [NO_GLOB_FLAG, END_OF_OPTIONS, ...staged_files]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function plan_missing(is_required: boolean): ScanDecision {
|
|
52
|
+
return is_required
|
|
53
|
+
? { kind: 'fail', error: REQUIRED_MISSING_ERROR }
|
|
54
|
+
: { kind: 'skip', notice: MISSING_NOTICE }
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
function plan_scan(
|
|
57
|
+
function plan_scan(
|
|
58
|
+
staged_files: ReadonlyArray<string>,
|
|
59
|
+
project_root: string,
|
|
60
|
+
is_required = false,
|
|
61
|
+
): ScanDecision {
|
|
40
62
|
if (staged_files.length === 0) return { kind: 'skip' }
|
|
41
63
|
|
|
42
64
|
const binary = resolve_secretlint_binary(project_root)
|
|
43
65
|
|
|
44
|
-
if (binary === undefined) return
|
|
66
|
+
if (binary === undefined) return plan_missing(is_required)
|
|
45
67
|
|
|
46
68
|
return { kind: 'scan', binary }
|
|
47
69
|
}
|
|
@@ -59,18 +81,29 @@ function run_scan(binary: string, staged_files: ReadonlyArray<string>): number {
|
|
|
59
81
|
return resolve_spawn_exit(binary, result)
|
|
60
82
|
}
|
|
61
83
|
|
|
62
|
-
function main(
|
|
63
|
-
|
|
84
|
+
function main(
|
|
85
|
+
staged_files: ReadonlyArray<string>,
|
|
86
|
+
project_root: string,
|
|
87
|
+
is_required = false,
|
|
88
|
+
): number {
|
|
89
|
+
const decision = plan_scan(staged_files, project_root, is_required)
|
|
64
90
|
|
|
65
91
|
if (decision.kind === 'scan') return run_scan(decision.binary, staged_files)
|
|
66
92
|
|
|
93
|
+
if (decision.kind === 'fail') {
|
|
94
|
+
console.error(decision.error)
|
|
95
|
+
|
|
96
|
+
return 1
|
|
97
|
+
}
|
|
98
|
+
|
|
67
99
|
if (decision.notice !== undefined) console.warn(decision.notice)
|
|
68
100
|
|
|
69
101
|
return 0
|
|
70
102
|
}
|
|
71
103
|
|
|
72
104
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
73
|
-
const
|
|
105
|
+
const is_required = process.env[REQUIRE_ENV] === '1'
|
|
106
|
+
const exit_code = main(process.argv.slice(ARGV_OFFSET), process.cwd(), is_required)
|
|
74
107
|
|
|
75
108
|
if (exit_code !== 0) process.exit(exit_code)
|
|
76
109
|
}
|
|
@@ -83,4 +116,4 @@ const secretlint_scan = {
|
|
|
83
116
|
run_scan,
|
|
84
117
|
}
|
|
85
118
|
|
|
86
|
-
export { MISSING_NOTICE, secretlint_scan }
|
|
119
|
+
export { MISSING_NOTICE, REQUIRE_ENV, REQUIRED_MISSING_ERROR, secretlint_scan }
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { session_owner_pin } from '#scripts/discovery/session-owner-pin'
|
|
3
4
|
import { security_audit_provision } from './security-audit-provision'
|
|
4
5
|
|
|
5
6
|
// The `josh audit:provision` entry and the session-start hook's bundle, kept apart from
|
|
6
7
|
// `security-audit-provision.ts` because `start-steps.ts` imports that one: a bundle is one file, so a
|
|
7
8
|
// main guard there would fire for every command that carries it (joshuafolkken/kit#4186).
|
|
9
|
+
//
|
|
10
|
+
// Run by the session-start hook (`--session-start`), it also pins the session's owner
|
|
11
|
+
// (`session-owner-pin.ts`) at no spawn of its own. A manual `josh audit:provision` pins only when
|
|
12
|
+
// passed `--session-start`, which re-pins to whatever owner `origin` names at that moment.
|
|
8
13
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
9
14
|
const ARGV_OFFSET = 2
|
|
15
|
+
const SESSION_START_FLAG = '--session-start'
|
|
16
|
+
const argv = process.argv.slice(ARGV_OFFSET)
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
if (argv.includes(SESSION_START_FLAG)) session_owner_pin.pin()
|
|
19
|
+
await security_audit_provision.run(argv)
|
|
12
20
|
}
|
|
@@ -140,7 +140,7 @@ async function report(
|
|
|
140
140
|
is_forced: boolean,
|
|
141
141
|
): Promise<string> {
|
|
142
142
|
const logic = security_audit_provision_logic
|
|
143
|
-
const target_path =
|
|
143
|
+
const target_path = security_audit.managed_binary_path(project_root, platform)
|
|
144
144
|
const existing = existing_scanner(target_path)
|
|
145
145
|
if (existing !== undefined) return logic.format_already_present(existing)
|
|
146
146
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { accessSync, constants, statSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { git_common_directory } from '#scripts/git/git-common-directory'
|
|
2
4
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
3
5
|
import { execaSync } from 'execa'
|
|
4
6
|
import { security_audit_logic } from './security-audit-logic'
|
|
5
7
|
import { security_audit_provision_logic } from './security-audit-provision-logic'
|
|
6
8
|
|
|
7
9
|
const FAILURE_EXIT_CODE = 1
|
|
10
|
+
const GIT_DIRECTORY_NAME = '.git'
|
|
8
11
|
const UNKNOWN_VERSION = 'unknown'
|
|
9
12
|
|
|
10
13
|
// execa reports `exitCode: undefined` only when the binary could not be spawned
|
|
@@ -56,11 +59,27 @@ interface ScannerChoice {
|
|
|
56
59
|
is_below_floor: boolean
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
// A linked work tree shares the primary checkout's managed scanner, so a lane neither downloads its
|
|
63
|
+
// own copy at every session start nor keeps its own no-retry record (joshuafolkken/kit#4942). Where
|
|
64
|
+
// the primary checkout cannot be named — a bare repository, a submodule, or no repository — the root
|
|
65
|
+
// is the project's own. The primary checkout reads git's answer too, so it keys the no-retry record
|
|
66
|
+
// on the same real path its lanes do even when it was entered through a symlink.
|
|
67
|
+
function managed_root(project_root: string): string {
|
|
68
|
+
const common = git_common_directory.repository(project_root)
|
|
69
|
+
if (common === undefined || path.basename(common) !== GIT_DIRECTORY_NAME) return project_root
|
|
70
|
+
|
|
71
|
+
return path.dirname(common)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function managed_binary_path(project_root: string, platform: string): string {
|
|
75
|
+
return security_audit_logic.build_managed_binary_path(managed_root(project_root), platform)
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
// A provisioned copy counts only when it is both a real executable and at or above the floor: a
|
|
60
79
|
// leftover from an earlier pinned version would otherwise be preferred over the PATH binary while
|
|
61
80
|
// reading less of the lockfile than it should.
|
|
62
81
|
function managed_scanner(project_root: string, platform: string): string | undefined {
|
|
63
|
-
const managed_path =
|
|
82
|
+
const managed_path = managed_binary_path(project_root, platform)
|
|
64
83
|
|
|
65
84
|
return is_executable_file(managed_path) && meets_floor(managed_path) ? managed_path : undefined
|
|
66
85
|
}
|
|
@@ -132,6 +151,7 @@ function run(): number {
|
|
|
132
151
|
const security_audit = {
|
|
133
152
|
is_binary_available,
|
|
134
153
|
is_executable_file,
|
|
154
|
+
managed_binary_path,
|
|
135
155
|
meets_floor,
|
|
136
156
|
resolve_scanner,
|
|
137
157
|
run,
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import path from 'node:path'
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
3
2
|
import { init_logic } from '#scripts/init/init-logic'
|
|
4
3
|
import type { SonarIdentifiers } from '#scripts/init/init-logic-sonar'
|
|
4
|
+
import { file_content } from '#scripts/sync/file-content'
|
|
5
|
+
|
|
6
|
+
// Written through `write_contained` like every other sync write, so a `sonar-project.properties`
|
|
7
|
+
// linked outside the project is refused rather than written through (joshuafolkken/kit#4634).
|
|
5
8
|
|
|
6
9
|
function write_sonar_file(
|
|
7
10
|
template_source: string,
|
|
@@ -14,8 +17,7 @@ function write_sonar_file(
|
|
|
14
17
|
identifiers.organization,
|
|
15
18
|
)
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
writeFileSync(destination_path, content)
|
|
20
|
+
file_content.write_contained(destination_path, content)
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
function merge_sonar_file(
|
|
@@ -36,7 +38,10 @@ function merge_sonar_file(
|
|
|
36
38
|
)
|
|
37
39
|
const existing = readFileSync(destination_path, 'utf8')
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
file_content.write_contained(
|
|
42
|
+
destination_path,
|
|
43
|
+
init_logic.merge_sonar_properties(existing, template_content),
|
|
44
|
+
)
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
const sonar_file = { write_sonar_file, merge_sonar_file }
|
|
@@ -76,7 +76,8 @@ function non_test_changes(changes: ReadonlyArray<FileChange>): Array<FileChange>
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// `split` only when **both** guides are exceeded together — the size condition a split has to clear,
|
|
79
|
-
// per `split-assessment.md`'s conservative default. Either one alone leaves the change one Issue.
|
|
79
|
+
// per `split-assessment.md`'s conservative default. Either one alone leaves the change one Issue. A
|
|
80
|
+
// filing's declared estimate is judged by this same call (`issue/size-estimate.ts`).
|
|
80
81
|
function verdict_for(files: number, lines: number): SplitVerdict {
|
|
81
82
|
return files > FILE_GUIDE && lines > LINE_GUIDE ? SPLIT_VERDICT : SINGLE_VERDICT
|
|
82
83
|
}
|
|
@@ -107,9 +108,14 @@ function reason(measurement: SizeMeasurement): string {
|
|
|
107
108
|
return `${counted} (${guide}) — ${tail}`
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
// The `split-assessment.md`
|
|
111
|
+
// The `split-assessment.md` headings the notice and a filing refusal point at; the document-rule test
|
|
112
|
+
// pins the document to both.
|
|
111
113
|
const NOTICE_SECTION = 'Over the guide before the gate'
|
|
112
|
-
const
|
|
114
|
+
const QUESTION_SECTION = 'The question'
|
|
115
|
+
// The line that records why an Issue stays one — read the same in a filed body (`size_estimate`) and
|
|
116
|
+
// in the Issue comment that answers the notice (`split_resolution`).
|
|
117
|
+
const REASON_PREFIX = '- 分割しない理由:'
|
|
118
|
+
const NOTICE_ACTION = `record why it stays one Issue in an Issue comment line \`${REASON_PREFIX} <理由>\`, or carve the rest out with \`pnpm josh issue:file --route split\` — split-assessment.md → "${NOTICE_SECTION}"`
|
|
113
119
|
|
|
114
120
|
// The pre-gate notice `josh main:merge` prints for a diff over the guide. "Clearly" is the `split`
|
|
115
121
|
// verdict — both guides exceeded — so the one bar this file draws is the one the notice fires on,
|
|
@@ -124,12 +130,15 @@ const split_assess = {
|
|
|
124
130
|
FILE_GUIDE,
|
|
125
131
|
LINE_GUIDE,
|
|
126
132
|
NOTICE_SECTION,
|
|
133
|
+
QUESTION_SECTION,
|
|
134
|
+
REASON_PREFIX,
|
|
127
135
|
SPLIT_VERDICT,
|
|
128
136
|
SINGLE_VERDICT,
|
|
129
137
|
assess,
|
|
130
138
|
notice,
|
|
131
139
|
parse_numstat,
|
|
132
140
|
reason,
|
|
141
|
+
verdict_for,
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
export type { FileChange, SizeMeasurement, SplitVerdict }
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { writeFile } from 'node:fs/promises'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { git_branch } from '#scripts/git/git-branch'
|
|
5
|
+
import { git_command } from '#scripts/git/git-command'
|
|
6
|
+
import { lane_ledger_write } from '#scripts/lane/lane-ledger-write'
|
|
7
|
+
import { split_assess, type SizeMeasurement } from './split-assess'
|
|
8
|
+
import { split_measure } from './split-measure'
|
|
9
|
+
|
|
10
|
+
// **The split notice, shown mid-implementation** (joshuafolkken/kit#4642). `josh main:merge` prints it
|
|
11
|
+
// only before the gate, when the run can no longer carve anything out; a lane that grew to 17 files met
|
|
12
|
+
// it 25 minutes in. The first `josh lint:related` or `josh test:related` after the branch's diff passes
|
|
13
|
+
// the guide prints the same notice — `split_assess.notice`, so the bar and the question are one
|
|
14
|
+
// definition — once per Issue: a marker in the work tree's git directory, created exclusively, records
|
|
15
|
+
// that it was shown. A diff under the guide leaves no marker, so the first crossing still prints.
|
|
16
|
+
// Advisory only: an unmeasurable diff or a branch with no Issue prints nothing.
|
|
17
|
+
|
|
18
|
+
const MARKER_PREFIX = 'josh-split-notice-'
|
|
19
|
+
const EXISTS_CODE = 'EEXIST'
|
|
20
|
+
|
|
21
|
+
async function marker_path(issue_number: number): Promise<string | undefined> {
|
|
22
|
+
const [git_directory] = await git_command.git_directories()
|
|
23
|
+
|
|
24
|
+
if (git_directory === undefined) return undefined
|
|
25
|
+
|
|
26
|
+
return path.join(git_directory, `${MARKER_PREFIX}${String(issue_number)}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Undefined once shown, so a later run skips the measurement — a diff against main and a read of
|
|
30
|
+
// every untracked file.
|
|
31
|
+
async function unclaimed_marker(issue_number: number): Promise<string | undefined> {
|
|
32
|
+
const target = await marker_path(issue_number)
|
|
33
|
+
|
|
34
|
+
return target === undefined || existsSync(target) ? undefined : target
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function is_exists_error(error: unknown): boolean {
|
|
38
|
+
return error instanceof Error && 'code' in error && error.code === EXISTS_CODE
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// True only for the call that created the marker — the one that shows the notice.
|
|
42
|
+
async function claim(target: string): Promise<boolean> {
|
|
43
|
+
try {
|
|
44
|
+
await writeFile(target, '', { flag: 'wx' })
|
|
45
|
+
|
|
46
|
+
return true
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (is_exists_error(error)) return false
|
|
49
|
+
throw error
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The claim that shows the notice first also writes it to the lane ledger, so the ledger holds one
|
|
54
|
+
// line per Issue, whichever command showed it (joshuafolkken/kit#4695).
|
|
55
|
+
async function claim_notice(
|
|
56
|
+
target: string,
|
|
57
|
+
issue: number,
|
|
58
|
+
measurement: SizeMeasurement,
|
|
59
|
+
): Promise<boolean> {
|
|
60
|
+
if (!(await claim(target))) return false
|
|
61
|
+
await lane_ledger_write.record_split_notice({
|
|
62
|
+
issue,
|
|
63
|
+
files: measurement.files,
|
|
64
|
+
lines: measurement.lines,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function branch_issue(): Promise<number | undefined> {
|
|
71
|
+
return git_branch.issue_from_branch(await git_command.branch())
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function pending_notice(): Promise<string | undefined> {
|
|
75
|
+
const issue_number = await branch_issue()
|
|
76
|
+
|
|
77
|
+
if (issue_number === undefined) return undefined
|
|
78
|
+
const target = await unclaimed_marker(issue_number)
|
|
79
|
+
|
|
80
|
+
if (target === undefined) return undefined
|
|
81
|
+
const measurement = await split_measure.read_branch()
|
|
82
|
+
const notice = split_assess.notice(measurement)
|
|
83
|
+
|
|
84
|
+
return notice !== undefined && (await claim_notice(target, issue_number, measurement))
|
|
85
|
+
? notice
|
|
86
|
+
: undefined
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// `josh main:merge` prints its notice on every merge; this records the first showing, as the scoped
|
|
90
|
+
// checks do, when neither of them showed it before. Advisory, as `surface` is.
|
|
91
|
+
async function record(measurement: SizeMeasurement): Promise<void> {
|
|
92
|
+
try {
|
|
93
|
+
const issue_number = await branch_issue()
|
|
94
|
+
const target = issue_number === undefined ? undefined : await unclaimed_marker(issue_number)
|
|
95
|
+
|
|
96
|
+
if (issue_number !== undefined && target !== undefined) {
|
|
97
|
+
await claim_notice(target, issue_number, measurement)
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
// Advisory: the notice was printed; only its record is lost.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function surface(): Promise<void> {
|
|
105
|
+
try {
|
|
106
|
+
const notice = await pending_notice()
|
|
107
|
+
|
|
108
|
+
if (notice !== undefined) console.warn(`\n${notice}`)
|
|
109
|
+
} catch {
|
|
110
|
+
// Advisory: `josh main:merge` reports an unmeasurable diff before the gate.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const split_early_notice = { MARKER_PREFIX, record, surface }
|
|
115
|
+
|
|
116
|
+
export { split_early_notice }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { change_base } from '#scripts/git/change-base'
|
|
3
4
|
import { changed_paths } from '#scripts/git/changed-paths'
|
|
4
5
|
import { git_command } from '#scripts/git/git-command'
|
|
5
6
|
import { git_spawn } from '#scripts/git/git-spawn'
|
|
@@ -14,7 +15,16 @@ import { split_assess } from './split-assess'
|
|
|
14
15
|
// `git diff --numstat` counts it. An entry with no readable content — a dangling symlink, a nested
|
|
15
16
|
// repository's directory — still counts as a file, of zero lines, so one such entry cannot reject
|
|
16
17
|
// the whole measurement.
|
|
17
|
-
|
|
18
|
+
//
|
|
19
|
+
// joshuafolkken/kit#4696: **the tracked half is measured from the tree's base, through a merge in
|
|
20
|
+
// progress** (`change_base.resolved_through_merge`). A merge of the default branch stopped on a
|
|
21
|
+
// conflict has already put the default branch's files in the work tree while `HEAD` is still the
|
|
22
|
+
// commit before it, so measuring from `HEAD`'s merge-base counted everything the merge brought in as
|
|
23
|
+
// this branch's own — 53 files where the branch had 10. A base that cannot be resolved throws, so no
|
|
24
|
+
// caller reads it as under the guide: the notices report the diff as unmeasured and `split:assess`
|
|
25
|
+
// fails outright.
|
|
26
|
+
|
|
27
|
+
const UNRESOLVED_BASE = 'the change base could not be resolved'
|
|
18
28
|
const NUL_BYTE = 0
|
|
19
29
|
const NO_CONTENT = Buffer.alloc(0)
|
|
20
30
|
const NEWLINE = '\n'
|
|
@@ -54,8 +64,16 @@ async function untracked_numstat(): Promise<string> {
|
|
|
54
64
|
return rows.join(NEWLINE)
|
|
55
65
|
}
|
|
56
66
|
|
|
67
|
+
async function tracked_numstat(): Promise<string> {
|
|
68
|
+
const base = await change_base.resolved_through_merge()
|
|
69
|
+
|
|
70
|
+
if (base === undefined) throw new Error(UNRESOLVED_BASE)
|
|
71
|
+
|
|
72
|
+
return await git_command.diff_numstat_since(base)
|
|
73
|
+
}
|
|
74
|
+
|
|
57
75
|
async function read_branch(): Promise<ReturnType<typeof split_assess.assess>> {
|
|
58
|
-
const tracked = await
|
|
76
|
+
const tracked = await tracked_numstat()
|
|
59
77
|
|
|
60
78
|
return split_assess.assess(`${tracked}${NEWLINE}${await untracked_numstat()}`)
|
|
61
79
|
}
|