@joshuafolkken/kit 1.1784.0 → 1.2114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/reviewer-verification.md +19 -0
- package/.claude/agents/reviewer.md +19 -0
- package/.claude/settings.json +5 -6
- package/.claude/skills/dependency-update/SKILL.md +9 -10
- package/.claude/skills/workflow-commands/SKILL.md +6 -6
- package/.claude/skills/workflow-commands/background-commands.md +8 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +41 -32
- package/.claude/skills/workflow-commands/delegation.md +6 -6
- package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +7 -5
- package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +9 -4
- package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -2
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
- package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
- package/.claude/skills/workflow-commands/prerequisite.md +3 -2
- package/.claude/skills/workflow-commands/retrospective.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +7 -5
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-pnpm/action.yml +9 -16
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/auto-tag.yml +45 -0
- package/.github/workflows/ci.yml +155 -15
- package/.github/workflows/dependabot-auto-merge.yml +63 -4
- package/.github/workflows/production.yml +8 -0
- package/.github/workflows/publish.yml +59 -45
- package/.vscode/settings.json +19 -0
- package/CLAUDE.md +5 -12
- package/SECURITY.md +2 -2
- package/dist/CLAUDE.md +5 -12
- package/dist/commands/document-read-cli.js +2 -1
- package/dist/commands/document-section-main.js +2 -1
- package/dist/commands/read-set-cli.js +194 -134
- package/dist/commands/run-prep-main.js +9694 -10173
- package/dist/commands/run-step-cli.js +22138 -18285
- package/dist/hooks/audit-provision.js +23 -5
- package/dist/hooks/chunk-3GKE653K.js +6204 -0
- package/dist/hooks/chunk-45CJJOO7.js +1243 -0
- package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
- package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
- package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
- package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
- package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
- package/dist/hooks/chunk-VVNYJWEI.js +30 -0
- package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
- package/dist/hooks/codex-hook-adapter.js +8 -8
- package/dist/hooks/format-edited.js +4 -5
- package/dist/hooks/inputs.json +415 -389
- package/dist/hooks/pretool-guard.js +7 -6
- package/dist/hooks/session-lang.js +101 -258
- package/dist/hooks/stop-guard.js +1744 -2576
- package/dist/josh.js +7133 -6368
- package/dist/version/index.js +161 -1
- package/eslint/rules/comment-why-only.js +108 -0
- package/package.json +15 -8
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +14 -11
- package/prompts/collaboration-workflow/issue-citation.md +0 -4
- package/prompts/collaboration-workflow/issue-template.md +40 -21
- package/prompts/collaboration-workflow/lane-instructions.md +3 -1
- package/prompts/collaboration-workflow/measurement.md +8 -1
- package/prompts/collaboration-workflow/operating-rules.md +11 -2
- package/prompts/collaboration-workflow/output-bounds.md +6 -2
- package/prompts/collaboration-workflow/plan-comment.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/residency.md +3 -2
- package/prompts/collaboration-workflow/rule-delivery.md +13 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +3 -1
- package/prompts/review-rubric.md +8 -0
- package/prompts/review.md +31 -22
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-argv.ts +14 -16
- package/scripts/agent/agent-provider.ts +138 -0
- package/scripts/agent/agent-role-profile.ts +108 -156
- package/scripts/agent/agent-safe-text.ts +20 -5
- package/scripts/agent/api-usage-limit.ts +65 -0
- package/scripts/agent/claude-agent-argv.ts +19 -9
- package/scripts/agent/codex-agent-argv.ts +118 -22
- package/scripts/agent/usage-limit-reset.ts +179 -0
- package/scripts/auth/claude-token-output.ts +2 -2
- package/scripts/auto-ok/auto-ok-author.ts +53 -0
- package/scripts/auto-ok/auto-ok-cli.ts +20 -9
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +6 -1
- package/scripts/backlog/backlog-drive-hold.ts +3 -3
- package/scripts/backlog/backlog-drive-idle.ts +10 -0
- package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
- package/scripts/backlog/backlog-drive-launch.ts +8 -2
- package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
- package/scripts/backlog/backlog-drive-named.ts +28 -7
- package/scripts/backlog/backlog-drive-paused.ts +5 -4
- package/scripts/backlog/backlog-drive-restore.ts +11 -3
- package/scripts/backlog/backlog-drive.ts +21 -60
- package/scripts/backlog/backlog-gate-scope.ts +61 -0
- package/scripts/backlog/backlog-measure-close.ts +112 -0
- package/scripts/backlog/backlog-next.ts +39 -49
- package/scripts/backlog/backlog-occupied.ts +2 -2
- package/scripts/backlog/backlog-overlap.ts +42 -8
- package/scripts/backlog/backlog-plan-cli.ts +4 -1
- package/scripts/backlog/backlog-plan-comment.ts +81 -0
- package/scripts/backlog/backlog-plan-read.ts +19 -6
- package/scripts/backlog/backlog-pool.ts +50 -12
- package/scripts/backlog/backlog-rank.ts +60 -13
- package/scripts/backlog/backlog-stalled-detect.ts +2 -1
- package/scripts/backlog/backlog-waves.ts +17 -2
- package/scripts/backlog/backlogrun-launch.ts +7 -4
- package/scripts/cost-runtime/context-cut-payback.ts +27 -25
- package/scripts/cost-runtime/cost-cli.ts +37 -16
- package/scripts/cost-runtime/cost-pricing.ts +9 -0
- package/scripts/cost-runtime/cost-verdict.ts +4 -1
- package/scripts/delegation/delegation-policy.ts +2 -2
- package/scripts/delegation/duplicate-read-guard.ts +1 -0
- package/scripts/delegation/investigation-guard.ts +14 -2
- package/scripts/delegation/investigation-reads.ts +45 -23
- package/scripts/dependabot/dependabot-cooldown.ts +3 -2
- package/scripts/discovery/repo-party.ts +80 -20
- package/scripts/discovery/session-owner-pin.ts +51 -0
- package/scripts/document/capped-print.ts +1 -1
- package/scripts/document/command-reference-line-budget.ts +42 -0
- package/scripts/document/document-byte-budget.ts +17 -7
- package/scripts/document/document-checks.ts +20 -0
- package/scripts/document/document-ratchet-check.ts +101 -0
- package/scripts/document/document-section.ts +1 -0
- package/scripts/document/entry-read-budget.ts +7 -3
- package/scripts/document/issue-citation-budget.ts +94 -0
- package/scripts/epic/epic-cli-argv.ts +2 -2
- package/scripts/epic/epic-fetch.ts +4 -2
- package/scripts/epic/epic-graph.ts +7 -2
- package/scripts/epic/epic-issue.ts +3 -1
- package/scripts/epic/epic-rank.ts +5 -0
- package/scripts/exports/unused-members-check.ts +62 -0
- package/scripts/exports/unused-members-cli.ts +6 -51
- package/scripts/followup/followup-ci-path.ts +67 -0
- package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
- package/scripts/followup/followup-coderabbit-wait.ts +13 -10
- package/scripts/followup/followup-failed-tests.ts +160 -0
- package/scripts/followup/followup-job-needs.ts +100 -0
- package/scripts/followup/followup-job-rerun.ts +303 -0
- package/scripts/followup/followup-main-move.ts +113 -0
- package/scripts/followup/git-followup-finish.ts +43 -11
- package/scripts/followup/git-followup-pending.ts +3 -3
- package/scripts/followup/git-followup-stages.ts +41 -11
- package/scripts/followup/git-pr-followup-checks.ts +55 -16
- package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
- package/scripts/followup/git-pr-followup.ts +62 -27
- package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
- package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
- package/scripts/gate/busy-average.ts +34 -2
- package/scripts/gate/check-related-pinned.ts +43 -0
- package/scripts/gate/check-related-reuse.ts +64 -0
- package/scripts/gate/check-related-section.ts +57 -0
- package/scripts/gate/check-related-unplanned.ts +34 -0
- package/scripts/gate/check-related.ts +265 -0
- package/scripts/gate/command-memory.ts +1 -1
- package/scripts/gate/core-admission.ts +24 -6
- package/scripts/gate/core-budget-timing.ts +20 -6
- package/scripts/gate/core-budget.ts +40 -11
- package/scripts/gate/core-wait-record.ts +3 -2
- package/scripts/gate/core-wait.ts +25 -2
- package/scripts/gate/core-weights.ts +15 -7
- package/scripts/gate/gate-labels.ts +46 -0
- package/scripts/gate/gate-ledger.ts +14 -6
- package/scripts/gate/gate-plan.ts +103 -58
- package/scripts/gate/gate-report.ts +9 -7
- package/scripts/gate/gate-run-steps.ts +26 -3
- package/scripts/gate/gate-skip.ts +45 -6
- package/scripts/gate/gate-static-group.ts +60 -0
- package/scripts/gate/gate-steps.ts +2 -1
- package/scripts/gate/gate-tree.ts +4 -2
- package/scripts/gate/hook-gate-reuse.ts +19 -2
- package/scripts/gate/metrics-totals.ts +59 -0
- package/scripts/gate/scoped-green.ts +68 -15
- package/scripts/gate/since-green.ts +14 -6
- package/scripts/gate/structural-checks.ts +96 -0
- package/scripts/gate/verification-gate.ts +51 -16
- package/scripts/gate/verification-inputs.ts +21 -6
- package/scripts/gate/verification-tail.ts +52 -0
- package/scripts/gh/gh-author-association.ts +23 -2
- package/scripts/gh/git-ai-review-scan.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +12 -3
- package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
- package/scripts/gh/git-gh-exec.ts +32 -2
- package/scripts/gh/git-gh-issue-read.ts +4 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +8 -2
- package/scripts/gh/git-gh-issue.ts +12 -5
- package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
- package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
- package/scripts/gh/git-gh-pr-read.ts +74 -13
- package/scripts/gh/git-gh-pr-rest.ts +3 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
- package/scripts/gh/git-gh-pr-too-large.ts +20 -0
- package/scripts/gh/git-gh-pr.ts +21 -7
- package/scripts/gh/git-gh-retry.ts +141 -0
- package/scripts/gh/git-pr-ai-review.ts +15 -6
- package/scripts/gh/git-pr-checks-eval.ts +3 -0
- package/scripts/gh/git-pr-checks.ts +5 -5
- package/scripts/git/base-tree.ts +30 -0
- package/scripts/git/change-base.ts +4 -10
- package/scripts/git/changed-paths-sync.ts +63 -0
- package/scripts/git/changed-paths.ts +16 -1
- package/scripts/git/git-common-directory.ts +11 -1
- package/scripts/git/git-diff-reads.ts +66 -20
- package/scripts/git/git-error.ts +9 -2
- package/scripts/git/git-schemas.ts +13 -0
- package/scripts/git/git-worktree.ts +13 -0
- package/scripts/git/gone-branch.ts +4 -1
- package/scripts/git/main-merge-budget-conflict.ts +89 -0
- package/scripts/git/main-merge-guard.ts +55 -18
- package/scripts/git/main-merge-regenerate.ts +59 -25
- package/scripts/git/main-merge.ts +53 -5
- package/scripts/git/no-base-checkout.ts +127 -0
- package/scripts/git/stash/git-stash.ts +11 -2
- package/scripts/git/stash/stash-push-cli.ts +10 -0
- package/scripts/hooks/batch-guard.ts +40 -21
- package/scripts/hooks/edited-cspell.ts +37 -11
- package/scripts/hooks/format-edited-file.ts +22 -2
- package/scripts/hooks/lane-plan-guard.ts +50 -0
- package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
- package/scripts/hooks/pre-commit-lint.ts +14 -1
- package/scripts/hooks/pre-push-related-switch.ts +10 -0
- package/scripts/hooks/pre-push-unit.ts +76 -10
- package/scripts/hooks/pretool-guard.ts +2 -0
- package/scripts/hooks/reserved-run.ts +14 -1
- package/scripts/hooks/run-hook.sh +23 -8
- package/scripts/hooks/step-zero-notice.ts +6 -1
- package/scripts/hooks/stop-actions.ts +3 -15
- package/scripts/hooks/stop-guard.ts +7 -6
- package/scripts/init/distributed-paths.ts +1 -0
- package/scripts/init/hook-command-rewrite.ts +5 -1
- package/scripts/init/hook-launch.ts +4 -2
- package/scripts/issue/aggregation-split.ts +52 -0
- package/scripts/issue/defect-origin-line.ts +19 -1
- package/scripts/issue/defect-rate-cli.ts +5 -102
- package/scripts/issue/defect-report.ts +4 -4
- package/scripts/issue/effect-audit-cli.ts +178 -0
- package/scripts/issue/effect-audit.ts +185 -0
- package/scripts/issue/expected-effect-lint.ts +64 -9
- package/scripts/issue/git-next-issues.ts +5 -0
- package/scripts/issue/issue-body-or-comment.ts +38 -4
- package/scripts/issue/issue-cite-cli.ts +1 -3
- package/scripts/issue/issue-classification.ts +1 -0
- package/scripts/issue/issue-closing-pr.ts +8 -1
- package/scripts/issue/issue-evidence-exit.ts +4 -2
- package/scripts/issue/issue-file-cap.ts +58 -0
- package/scripts/issue/issue-file-cli.ts +111 -38
- package/scripts/issue/issue-file-deferral.ts +52 -0
- package/scripts/issue/issue-file-fold.ts +29 -10
- package/scripts/issue/issue-file-interrupt.ts +85 -0
- package/scripts/issue/issue-file-lane-hint.ts +30 -0
- package/scripts/issue/issue-file.ts +43 -6
- package/scripts/issue/issue-labels.ts +19 -2
- package/scripts/issue/issue-lint-cli.ts +27 -2
- package/scripts/issue/issue-measured-cli.ts +13 -35
- package/scripts/issue/issue-measured-close.ts +58 -0
- package/scripts/issue/issue-not-before-label.ts +2 -2
- package/scripts/issue/issue-not-before.ts +4 -1
- package/scripts/issue/issue-priority-cli.ts +60 -0
- package/scripts/issue/issue-priority-raise.ts +55 -0
- package/scripts/issue/issue-priority.ts +174 -43
- package/scripts/issue/issue-rank.ts +21 -4
- package/scripts/issue/issue-read-cli.ts +32 -6
- package/scripts/issue/issue-read.ts +37 -12
- package/scripts/issue/issue-sample-gate.ts +95 -74
- package/scripts/issue/issue-sample-prime.ts +127 -0
- package/scripts/issue/issue-sample-required.ts +57 -0
- package/scripts/issue/issue-sample-source.ts +12 -11
- package/scripts/issue/issue-search.ts +124 -0
- package/scripts/issue/issue-state.ts +13 -1
- package/scripts/issue/issue-template-section.ts +43 -0
- package/scripts/issue/issue-watch-diff.ts +31 -1
- package/scripts/issue/issue-watch-hook.ts +11 -2
- package/scripts/issue/issue-watch.ts +55 -12
- package/scripts/issue/markdown-section.ts +1 -0
- package/scripts/issue/measure-declaration.ts +121 -0
- package/scripts/issue/measure-rerun-cli.ts +7 -70
- package/scripts/issue/plan-consumers-section.ts +22 -1
- package/scripts/issue/reproduction-measure.ts +3 -1
- package/scripts/issue/size-estimate.ts +90 -0
- package/scripts/issue/trusted-measure.ts +84 -0
- package/scripts/josh/command-usage.ts +3 -2
- package/scripts/josh/durable-state-migrate.ts +105 -0
- package/scripts/josh/durable-state.ts +112 -0
- package/scripts/josh/josh-aliases.ts +95 -0
- package/scripts/josh/josh-command-map.ts +1 -82
- package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
- package/scripts/josh/josh-commands-ai.ts +5 -5
- package/scripts/josh/josh-commands-development.ts +59 -58
- package/scripts/josh/josh-commands-issue-write.ts +23 -4
- package/scripts/josh/josh-commands-issue.ts +32 -0
- package/scripts/josh/josh-commands-lane.ts +87 -4
- package/scripts/josh/josh-commands-lint.ts +16 -3
- package/scripts/josh/josh-commands-maintenance.ts +15 -0
- package/scripts/josh/josh-commands-review.ts +41 -6
- package/scripts/josh/josh-commands-ship.ts +56 -8
- package/scripts/josh/josh-commands-test-check.ts +80 -0
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/josh-environment-file.ts +43 -3
- package/scripts/josh/josh-in-process.ts +2 -2
- package/scripts/josh/josh-logic.ts +5 -3
- package/scripts/josh/josh-run.ts +19 -1
- package/scripts/josh/run-record-path.ts +15 -0
- package/scripts/josh/stamp-file.ts +11 -2
- package/scripts/lane/lane-adopt-cli.ts +98 -0
- package/scripts/lane/lane-adopt.ts +208 -0
- package/scripts/lane/lane-await.ts +5 -8
- package/scripts/lane/lane-baseline.ts +123 -0
- package/scripts/lane/lane-breakdown-cli.ts +64 -0
- package/scripts/lane/lane-breakdown-implement.ts +170 -0
- package/scripts/lane/lane-breakdown-laps.ts +107 -0
- package/scripts/lane/lane-breakdown-pinned.ts +76 -0
- package/scripts/lane/lane-breakdown-text.ts +145 -0
- package/scripts/lane/lane-breakdown.ts +239 -0
- package/scripts/lane/lane-check-stats.ts +29 -8
- package/scripts/lane/lane-child-invocation.ts +9 -2
- package/scripts/lane/lane-child-mark-keys.ts +12 -0
- package/scripts/lane/lane-child-marker.ts +20 -1
- package/scripts/lane/lane-ci-path-stats.ts +73 -0
- package/scripts/lane/lane-claim-stats.ts +84 -0
- package/scripts/lane/lane-close.ts +8 -3
- package/scripts/lane/lane-dispatch-log.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +2 -2
- package/scripts/lane/lane-gap-stats.ts +7 -6
- package/scripts/lane/lane-guard-policy.ts +7 -1
- package/scripts/lane/lane-implement-stats.ts +207 -0
- package/scripts/lane/lane-implement-subagents.ts +90 -0
- package/scripts/lane/lane-investigation-switch.ts +16 -0
- package/scripts/lane/lane-ledger-kind.ts +31 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
- package/scripts/lane/lane-ledger-schema.ts +119 -36
- package/scripts/lane/lane-ledger-write.ts +257 -0
- package/scripts/lane/lane-ledger.ts +31 -177
- package/scripts/lane/lane-load.ts +1 -1
- package/scripts/lane/lane-measure-cli.ts +176 -59
- package/scripts/lane/lane-net-read.ts +44 -0
- package/scripts/lane/lane-net-time.ts +212 -0
- package/scripts/lane/lane-open.ts +16 -7
- package/scripts/lane/lane-output.ts +2 -4
- package/scripts/lane/lane-overrun-prompt.ts +147 -0
- package/scripts/lane/lane-overrun.ts +274 -0
- package/scripts/lane/lane-paths.ts +24 -1
- package/scripts/lane/lane-pause-cli.ts +4 -1
- package/scripts/lane/lane-paused-spans.ts +97 -0
- package/scripts/lane/lane-plan-check.ts +207 -0
- package/scripts/lane/lane-plan-cli.ts +41 -0
- package/scripts/lane/lane-plan.ts +144 -0
- package/scripts/lane/lane-registry.ts +14 -1
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-round-one-stats.ts +51 -0
- package/scripts/lane/lane-sampler.ts +23 -4
- package/scripts/lane/lane-session-action.ts +93 -18
- package/scripts/lane/lane-session-broker.ts +49 -21
- package/scripts/lane/lane-session-reread-main.ts +14 -0
- package/scripts/lane/lane-session-reread.ts +51 -0
- package/scripts/lane/lane-ship-lap-stats.ts +74 -0
- package/scripts/lane/lane-sleep.ts +58 -0
- package/scripts/lane/lane-spent.ts +1 -1
- package/scripts/lane/lane-split-stats.ts +28 -0
- package/scripts/lane/lane-stage-stats.ts +13 -1
- package/scripts/lane/lane-state.ts +41 -9
- package/scripts/lane/lane-stats-tables.ts +58 -0
- package/scripts/lane/lane-stats.ts +21 -5
- package/scripts/lane/lane-stop-stats.ts +51 -19
- package/scripts/lane/lane-tail-stats.ts +32 -0
- package/scripts/lane/lane-transcript-calls.ts +67 -10
- package/scripts/lane/lane-transcript-stats.ts +44 -12
- package/scripts/lane/lane-value-cell.ts +72 -0
- package/scripts/lane/lane-value.ts +118 -0
- package/scripts/lane/lane-withdraw-closed.ts +124 -0
- package/scripts/lane/lane-withdraw.ts +108 -31
- package/scripts/lane/lane-yield.ts +85 -0
- package/scripts/lane/openai-lane-ship.ts +12 -11
- package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
- package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
- package/scripts/lane/openai-lane-supervisor.ts +5 -4
- package/scripts/lib/poll.ts +38 -9
- package/scripts/lib/settled-together.ts +10 -0
- package/scripts/lib/shell-quote.ts +21 -0
- package/scripts/lib/token-shape.ts +111 -0
- package/scripts/lint/eslint-daemon-sources.ts +79 -0
- package/scripts/lint/eslint-daemon.ts +45 -6
- package/scripts/lint/eslint-warm-cli.ts +7 -0
- package/scripts/lint/eslint-warm.ts +66 -0
- package/scripts/lint/import-graph.ts +141 -11
- package/scripts/lint/lint-gate-scope.ts +2 -2
- package/scripts/lint/lint-parallel.ts +54 -6
- package/scripts/lint/lint-related.ts +154 -33
- package/scripts/lint/rename-cli.ts +179 -0
- package/scripts/lint/rename-identifier.ts +192 -0
- package/scripts/lint/type-only-imports.ts +58 -0
- package/scripts/notify/telegram-notify.ts +12 -3
- package/scripts/observations/observation-record.ts +42 -16
- package/scripts/observations/observations-flush.ts +32 -9
- package/scripts/overrides/overrides-check.ts +9 -1
- package/scripts/release/release-approval-prune.ts +7 -1
- package/scripts/repo/required-checks-logic.ts +4 -1
- package/scripts/repo/ruleset-check.ts +4 -3
- package/scripts/report/markdown-shape.ts +28 -0
- package/scripts/report/report-format-reference.ts +15 -1
- package/scripts/report/report-labels.ts +12 -1
- package/scripts/report/report-lint-child-summary.ts +94 -0
- package/scripts/report/report-lint-cli.ts +26 -7
- package/scripts/report/report-lint.ts +2 -20
- package/scripts/review/prose-stale-cli.ts +88 -0
- package/scripts/review/prose-stale-scan.ts +152 -0
- package/scripts/review/review-attest.ts +73 -23
- package/scripts/review/review-brief-cli.ts +68 -19
- package/scripts/review/review-brief-paths.ts +22 -0
- package/scripts/review/review-brief.ts +14 -7
- package/scripts/review/review-comment-carry.ts +47 -0
- package/scripts/review/review-comment-round.ts +85 -22
- package/scripts/review/review-finding-ledger.ts +109 -7
- package/scripts/review/review-finding-text.ts +43 -0
- package/scripts/review/review-interaction.ts +11 -0
- package/scripts/review/review-lane-handover.ts +68 -0
- package/scripts/review/review-lint-cli.ts +57 -0
- package/scripts/review/review-lint-findings.ts +92 -0
- package/scripts/review/review-lint-sections.ts +80 -0
- package/scripts/review/review-lint.ts +221 -0
- package/scripts/review/review-prior-findings.ts +102 -0
- package/scripts/review/review-readers-scan.ts +4 -2
- package/scripts/review/review-record-cli.ts +177 -54
- package/scripts/review/review-record-code-round.ts +55 -0
- package/scripts/review/review-record-comment.ts +60 -0
- package/scripts/review/review-record-interaction.ts +35 -0
- package/scripts/review/review-record-pinned.ts +108 -0
- package/scripts/review/review-record.ts +25 -1
- package/scripts/review/review-reviewed-tree.ts +307 -0
- package/scripts/review/review-round-one-closure.ts +62 -0
- package/scripts/review/review-round-reuse.ts +106 -0
- package/scripts/review/review-round2-cli.ts +91 -29
- package/scripts/review/review-round2-skip-comment.ts +55 -0
- package/scripts/review/review-round2.ts +24 -8
- package/scripts/review/review-stamps.ts +7 -3
- package/scripts/review/review-stop-log.ts +20 -6
- package/scripts/review/review-stops-cli.ts +2 -1
- package/scripts/review/review-submit-cli.ts +101 -0
- package/scripts/rules/agent-model.ts +1 -1
- package/scripts/rules/bash-triggers.ts +3 -3
- package/scripts/rules/concise-reply.ts +88 -0
- package/scripts/rules/credential-body.ts +77 -0
- package/scripts/rules/decision-oracle.ts +1 -1
- package/scripts/rules/delivered-rules.ts +36 -17
- package/scripts/rules/destructive-command.ts +3 -11
- package/scripts/rules/direct-issue-comment.ts +34 -12
- package/scripts/rules/direct-issue-read.ts +191 -0
- package/scripts/rules/direct-pr-create.ts +4 -24
- package/scripts/rules/early-heartbeat.ts +2 -0
- package/scripts/rules/edit-tool.ts +27 -0
- package/scripts/rules/file-edit-rows.ts +17 -0
- package/scripts/rules/file-rewrite.ts +146 -0
- package/scripts/rules/firing-point.ts +3 -2
- package/scripts/rules/gh-api.ts +128 -7
- package/scripts/rules/gh-repo-selector.ts +84 -0
- package/scripts/rules/git-force.ts +2 -2
- package/scripts/rules/guard-remedy.ts +14 -5
- package/scripts/rules/implementation-cut.ts +61 -28
- package/scripts/rules/investigator-foreground.ts +231 -0
- package/scripts/rules/issue-citation.ts +15 -60
- package/scripts/rules/issue-comments.ts +9 -4
- package/scripts/rules/josh-git-bare.ts +1 -1
- package/scripts/rules/lane-background.ts +41 -13
- package/scripts/rules/lane-direct-tool.ts +107 -9
- package/scripts/rules/lane-interactive-ask.ts +4 -1
- package/scripts/rules/lane-park.ts +4 -2
- package/scripts/rules/lane-spilled-read.ts +90 -0
- package/scripts/rules/lane-split-park.ts +1 -1
- package/scripts/rules/lane-switch-main.ts +4 -4
- package/scripts/rules/last-prompt.ts +20 -47
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/permission-guards.ts +5 -1
- package/scripts/rules/piped-verification-shapes.ts +110 -0
- package/scripts/rules/piped-verification.ts +67 -17
- package/scripts/rules/poll-loop.ts +72 -9
- package/scripts/rules/pr-merge.ts +5 -5
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +88 -24
- package/scripts/rules/reply-language.ts +3 -17
- package/scripts/rules/reply-prose.ts +34 -0
- package/scripts/rules/repo-admin.ts +132 -0
- package/scripts/rules/reviewer-foreground.ts +88 -0
- package/scripts/rules/round-one-check-related.ts +120 -0
- package/scripts/rules/rule-body-guard.ts +5 -15
- package/scripts/rules/rule-value-cache.ts +53 -5
- package/scripts/rules/rule-value-candidates.ts +84 -0
- package/scripts/rules/rule-value-cli.ts +59 -11
- package/scripts/rules/rule-value.ts +17 -4
- package/scripts/rules/run-tail-rule.ts +1 -1
- package/scripts/rules/sed-in-place.ts +196 -0
- package/scripts/rules/shell-segments.ts +10 -2
- package/scripts/rules/stop-rule-rows.ts +1 -13
- package/scripts/rules/stop-rules.ts +33 -58
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +110 -23
- package/scripts/rules/timed-rerun.ts +1 -1
- package/scripts/rules/turn-filings.ts +90 -0
- package/scripts/rules/worktree-guard.ts +22 -7
- package/scripts/run/board/run-board-ending-reason.ts +6 -2
- package/scripts/run/board/run-board-fit.ts +24 -1
- package/scripts/run/board/run-board-found.ts +134 -0
- package/scripts/run/board/run-board-header.ts +8 -0
- package/scripts/run/board/run-board-labels.ts +11 -1
- package/scripts/run/board/run-board-lane-time.ts +1 -0
- package/scripts/run/board/run-board-link.ts +15 -2
- package/scripts/run/board/run-board-notes.ts +7 -1
- package/scripts/run/board/run-board-operation.ts +4 -2
- package/scripts/run/board/run-board-phase.ts +2 -5
- package/scripts/run/board/run-board-plan.ts +1 -1
- package/scripts/run/board/run-board-read.ts +46 -1
- package/scripts/run/board/run-board-reason.ts +20 -8
- package/scripts/run/board/run-board-render-checks.ts +3 -2
- package/scripts/run/board/run-board-render-drawn.ts +11 -0
- package/scripts/run/board/run-board-render-found.ts +157 -0
- package/scripts/run/board/run-board-render-lead.ts +9 -0
- package/scripts/run/board/run-board-render-legend.ts +19 -0
- package/scripts/run/board/run-board-render-tail.ts +1 -1
- package/scripts/run/board/run-board-render.ts +82 -18
- package/scripts/run/board/run-board-spin.ts +1 -3
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +39 -10
- package/scripts/run/carry/run-carry-ended.ts +3 -2
- package/scripts/run/carry/run-carry.ts +3 -2
- package/scripts/run/cut/run-cut-cli.ts +33 -133
- package/scripts/run/cut/run-cut-digest.ts +152 -0
- package/scripts/run/cut/run-cut-handoff.ts +52 -8
- package/scripts/run/cut/run-cut-report.ts +24 -3
- package/scripts/run/cut/run-cut-resume.ts +129 -0
- package/scripts/run/cut/run-cut-review.ts +105 -0
- package/scripts/run/cut/run-cut.ts +18 -7
- package/scripts/run/detached-launch.ts +1 -1
- package/scripts/run/entry/run-entry-cli.ts +88 -19
- package/scripts/run/entry/run-entry-spill.ts +83 -0
- package/scripts/run/entry/run-entry-stop.ts +5 -1
- package/scripts/run/entry/run-entry-trust.ts +91 -0
- package/scripts/run/event/run-event-baseline.ts +56 -0
- package/scripts/run/event/run-event-filed.ts +9 -3
- package/scripts/run/event/run-event-overrun.ts +197 -0
- package/scripts/run/event/run-event-render.ts +4 -0
- package/scripts/run/event/run-event-stream.ts +25 -3
- package/scripts/run/hold/run-stop-notify.ts +3 -8
- package/scripts/run/merge/run-merge-cap.ts +57 -0
- package/scripts/run/merge/run-merge-cli.ts +66 -23
- package/scripts/run/merge/run-merge-outcome.ts +2 -0
- package/scripts/run/merge/run-merge-ship.ts +13 -3
- package/scripts/run/merge/run-merge-signals.ts +40 -23
- package/scripts/run/merge/run-merge-steps.ts +56 -36
- package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
- package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
- package/scripts/run/merge/run-merge.ts +41 -14
- package/scripts/run/review/run-review-cli.ts +306 -20
- package/scripts/run/review/run-review-early-push.ts +121 -0
- package/scripts/run/review/run-review-join-read.ts +94 -0
- package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
- package/scripts/run/review/run-review-pinned-join.ts +216 -0
- package/scripts/run/review/run-review-pinned-steps.ts +383 -0
- package/scripts/run/review/run-review-pinned-tree.ts +37 -0
- package/scripts/run/review/run-review-pinned.ts +53 -0
- package/scripts/run/review/run-review-steps.ts +70 -34
- package/scripts/run/review/run-review.ts +26 -6
- package/scripts/run/run-parent-cut-hook.ts +3 -4
- package/scripts/run/run-prep.ts +1 -0
- package/scripts/run/run-stage-read.ts +2 -6
- package/scripts/run/run-step-cli.ts +17 -5
- package/scripts/run/run-step-lane-plan.ts +27 -0
- package/scripts/run/run-step-section.ts +26 -7
- package/scripts/run/run-step.ts +5 -2
- package/scripts/run/ship/run-ship-args.ts +19 -0
- package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
- package/scripts/run/ship/run-ship-cli.ts +131 -39
- package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
- package/scripts/run/ship/run-ship-detach.ts +10 -4
- package/scripts/run/ship/run-ship-handover.ts +106 -0
- package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
- package/scripts/run/ship/run-ship-lap.ts +63 -0
- package/scripts/run/ship/run-ship-next.ts +27 -26
- package/scripts/run/ship/run-ship-overlap.ts +8 -3
- package/scripts/run/ship/run-ship-place.ts +9 -2
- package/scripts/run/ship/run-ship-planned.ts +92 -0
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
- package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
- package/scripts/run/ship/run-ship-preflight.ts +16 -4
- package/scripts/run/ship/run-ship-probe.ts +23 -9
- package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
- package/scripts/run/ship/run-ship-relaunch.ts +51 -0
- package/scripts/run/ship/run-ship-return.ts +103 -29
- package/scripts/run/ship/run-ship-review-reason.ts +24 -0
- package/scripts/run/ship/run-ship-review-steps.ts +230 -59
- package/scripts/run/ship/run-ship-review.ts +171 -51
- package/scripts/run/ship/run-ship-reviewer.ts +138 -42
- package/scripts/run/ship/run-ship-scoped.ts +39 -10
- package/scripts/run/ship/run-ship-stage.ts +49 -13
- package/scripts/run/ship/run-ship-steps.ts +24 -4
- package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
- package/scripts/run/ship/run-ship-stop-context.ts +50 -13
- package/scripts/run/ship/run-ship-sync.ts +171 -30
- package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
- package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
- package/scripts/run/ship/run-ship-waiter.ts +114 -0
- package/scripts/run/ship/run-ship.ts +16 -2
- package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
- package/scripts/run/wake/run-wake-cli.ts +6 -113
- package/scripts/run/wake/run-wake-code.ts +53 -11
- package/scripts/run/wake/run-wake-driver.ts +5 -3
- package/scripts/run/wake/run-wake-finish.ts +137 -0
- package/scripts/run/wake/run-wake-loop.ts +5 -3
- package/scripts/run/wake/run-wake-ports.ts +11 -3
- package/scripts/run/wake/run-wake-session.ts +2 -0
- package/scripts/run/wake/run-wake.ts +3 -2
- package/scripts/safe-chain/ci-installer-pin.ts +49 -8
- package/scripts/safe-chain/project-config.ts +3 -2
- package/scripts/security/secretlint-scan.ts +42 -9
- package/scripts/security/security-audit-provision-main.ts +9 -1
- package/scripts/security/security-audit-provision.ts +1 -1
- package/scripts/security/security-audit.ts +21 -1
- package/scripts/security/sonar-file.ts +10 -5
- package/scripts/split/split-assess.ts +12 -3
- package/scripts/split/split-early-notice.ts +116 -0
- package/scripts/split/split-measure.ts +20 -2
- package/scripts/split/split-resolution.ts +107 -0
- package/scripts/test/direct-importers.ts +185 -0
- package/scripts/test/pure-files-entries.ts +83 -0
- package/scripts/test/related-heavy-tests.ts +81 -0
- package/scripts/test/test-no-base.ts +88 -0
- package/scripts/test/test-pinned-cache-logic.ts +363 -0
- package/scripts/test/test-pinned-cache.ts +337 -0
- package/scripts/test/test-pinned-emit.ts +49 -0
- package/scripts/test/test-pinned-functions.ts +116 -0
- package/scripts/test/test-pinned-ledger.ts +80 -0
- package/scripts/test/test-pinned-logic.ts +208 -20
- package/scripts/test/test-pinned-main.ts +84 -18
- package/scripts/test/test-pinned-pool.ts +73 -0
- package/scripts/test/test-pinned-probe.ts +254 -0
- package/scripts/test/test-pinned-prune-logic.ts +118 -0
- package/scripts/test/test-pinned-reach.ts +194 -0
- package/scripts/test/test-pinned-record.ts +196 -0
- package/scripts/test/test-pinned-rewire.ts +148 -0
- package/scripts/test/test-pinned-spawn.ts +81 -0
- package/scripts/test/test-pinned-stage.ts +70 -0
- package/scripts/test/test-pinned.ts +258 -47
- package/scripts/test/test-reach-cli.ts +61 -0
- package/scripts/test/test-read-map.ts +35 -11
- package/scripts/test/test-read-recording.ts +17 -5
- package/scripts/test/test-read-trace.ts +26 -8
- package/scripts/test/test-red-logic.ts +46 -7
- package/scripts/test/test-red.ts +34 -14
- package/scripts/test/test-related-claim.ts +37 -0
- package/scripts/test/test-related-main.ts +4 -1
- package/scripts/test/test-related-moved.ts +91 -0
- package/scripts/test/test-related-record.ts +43 -23
- package/scripts/test/test-related-scope.ts +99 -34
- package/scripts/test/test-related-synced.ts +135 -0
- package/scripts/test/test-related-touched.ts +84 -0
- package/scripts/test/test-related.ts +167 -83
- package/scripts/test/test-unit-guard.ts +28 -5
- package/scripts/test/unit-durations.ts +41 -0
- package/scripts/test/unit-worker-share.ts +5 -4
- package/scripts/test/vitest-cache-directory.ts +19 -0
- package/scripts/test/vitest-cli-excludes.ts +14 -0
- package/scripts/test/vitest-failure-summary.ts +111 -0
- package/scripts/test/whole-suite-files.ts +12 -1
- package/scripts/time-runtime/time-batch-guard.ts +42 -4
- package/scripts/time-runtime/time-bundle-call.ts +1 -1
- package/scripts/time-runtime/time-bundles.ts +36 -5
- package/scripts/time-runtime/time-markers.ts +2 -2
- package/scripts/time-runtime/time-search-call.ts +38 -0
- package/scripts/time-runtime/time-shell.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +17 -5
- package/scripts/version/development-engines-verdict.ts +124 -0
- package/scripts/version/latest-corepack.ts +2 -0
- package/templates/gitignore +2 -0
- package/templates/workflows/ci.yml +41 -0
- package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
- package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
- package/dist/hooks/chunk-TUIFS5E7.js +0 -72
- package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
- package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
- package/scripts/rules/filing-cap.ts +0 -139
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { cost_transcript, type SessionFile } from '#scripts/cost-runtime/cost-transcript'
|
|
4
4
|
import { delivered_rules } from './delivered-rules'
|
|
5
|
+
import { piped_verification_shapes, type ShapeCounts } from './piped-verification-shapes'
|
|
5
6
|
import { rule_value, type RuleReading } from './rule-value'
|
|
6
|
-
import { rule_value_cache } from './rule-value-cache'
|
|
7
|
+
import { rule_value_cache, type FreshReading } from './rule-value-cache'
|
|
8
|
+
import { rule_value_candidates } from './rule-value-candidates'
|
|
7
9
|
|
|
8
10
|
// `josh rule:value` — what each delivered rule earns on the channel that carries it, one row per rule.
|
|
9
11
|
// `rule_value.measure` was public but had no caller outside its own test, so
|
|
@@ -17,6 +19,8 @@ import { rule_value_cache } from './rule-value-cache'
|
|
|
17
19
|
|
|
18
20
|
const ARGV_OFFSET = 2
|
|
19
21
|
const REFRESH_FLAG = '--refresh'
|
|
22
|
+
const FRESH_FLAG = '--fresh'
|
|
23
|
+
const CACHED_NOTE = 'cached reading taken at'
|
|
20
24
|
const PERCENT_SIGN = '%'
|
|
21
25
|
const COLUMN_GAP = ' '
|
|
22
26
|
const NO_TARGETS = 'no measurement targets'
|
|
@@ -70,8 +74,10 @@ function gather_runs(cwd: string = process.cwd()): Generator<Array<string>> {
|
|
|
70
74
|
return runs_of(group_by_run(files).values())
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
// The `piped-verification` refusals are named by shape in the same pass, so the corpus is read once.
|
|
73
78
|
interface Tally {
|
|
74
79
|
runs: number
|
|
80
|
+
shapes: ShapeCounts
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
function* counted(
|
|
@@ -80,6 +86,7 @@ function* counted(
|
|
|
80
86
|
): Generator<ReadonlyArray<string>> {
|
|
81
87
|
for (const texts of runs) {
|
|
82
88
|
tally.runs += 1
|
|
89
|
+
for (const text of texts) piped_verification_shapes.count(text, tally.shapes)
|
|
83
90
|
yield texts
|
|
84
91
|
}
|
|
85
92
|
}
|
|
@@ -108,26 +115,65 @@ function row(reading: RuleReading): string {
|
|
|
108
115
|
return [id, reached, rate_text(reading), refused, ...rewritten].join(COLUMN_GAP)
|
|
109
116
|
}
|
|
110
117
|
|
|
118
|
+
// `candidates` is set only where something measurable was found, so `NO_TARGETS` stays the whole answer.
|
|
119
|
+
interface Rendered {
|
|
120
|
+
table: string
|
|
121
|
+
candidates?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
111
124
|
// The reading as text, or `NO_TARGETS` when nothing measurable was found — never an empty output that
|
|
112
125
|
// would read as a broken command.
|
|
113
126
|
// The runs are counted as `measure` consumes them, since a lazy source cannot be asked its length
|
|
114
127
|
// up front.
|
|
115
|
-
function
|
|
116
|
-
const tally: Tally = { runs: 0 }
|
|
128
|
+
function render_parts(runs: Iterable<ReadonlyArray<string>>): Rendered {
|
|
129
|
+
const tally: Tally = { runs: 0, shapes: new Map() }
|
|
117
130
|
const readings = rule_value.measure(counted(runs, tally))
|
|
118
131
|
|
|
119
|
-
if (tally.runs === 0) return NO_TARGETS
|
|
132
|
+
if (tally.runs === 0) return { table: NO_TARGETS }
|
|
120
133
|
|
|
121
|
-
|
|
134
|
+
const judged = rule_value_candidates.judge(readings, tally.runs)
|
|
135
|
+
const shapes = piped_verification_shapes.render(tally.shapes)
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
table: readings.map((reading) => row(reading)).join('\n'),
|
|
139
|
+
candidates: `${rule_value_candidates.render(judged, tally.runs)}\n${shapes}`,
|
|
140
|
+
}
|
|
122
141
|
}
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
function render(runs: Iterable<ReadonlyArray<string>>): string {
|
|
144
|
+
return render_parts(runs).table
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The cached table and when it was taken. The cache holds the table alone, so the line names the flag
|
|
148
|
+
// that measures now for the `candidates:` section too.
|
|
149
|
+
function cached_text(fresh: FreshReading): string {
|
|
150
|
+
const taken_at = fresh.taken_at.toISOString()
|
|
128
151
|
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
return `${fresh.text.trimEnd()}\n\n${CACHED_NOTE} ${taken_at} — ${FRESH_FLAG} to measure now, with candidates`
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function print_measured(cwd: string): void {
|
|
156
|
+
const { table, candidates } = render_parts(gather_runs(cwd))
|
|
157
|
+
|
|
158
|
+
process.stdout.write(`${[table, candidates].filter(Boolean).join('\n\n')}\n`)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function print_reading(argv: ReadonlyArray<string>, cwd: string): void {
|
|
162
|
+
const fresh = argv.includes(FRESH_FLAG) ? undefined : rule_value_cache.read_fresh(cwd)
|
|
163
|
+
|
|
164
|
+
if (fresh === undefined) print_measured(cwd)
|
|
165
|
+
else process.stdout.write(`${cached_text(fresh)}\n`)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// `--refresh` is the detached measurement the `backlogrun` loop head starts: the table goes to the
|
|
169
|
+
// cache `backlog:offer` prints from, never to stdout (`rule-value-cache.ts`). The candidates are read
|
|
170
|
+
// when a rule is retired, not at the loop head, so they print to stdout only and the cache keeps its
|
|
171
|
+
// shape. Bare, a reading cached inside the refresh interval is printed instead of measuring again —
|
|
172
|
+
// the measurement re-reads every transcript and takes minutes (joshuafolkken/kit#5186); `--fresh`
|
|
173
|
+
// measures regardless.
|
|
174
|
+
function run_rule_value(argv: ReadonlyArray<string> = [], cwd: string = process.cwd()): number {
|
|
175
|
+
if (argv.includes(REFRESH_FLAG)) rule_value_cache.store(render_parts(gather_runs(cwd)).table, cwd)
|
|
176
|
+
else print_reading(argv, cwd)
|
|
131
177
|
|
|
132
178
|
return 0
|
|
133
179
|
}
|
|
@@ -139,9 +185,11 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
|
139
185
|
const rule_value_cli = {
|
|
140
186
|
group_by_run,
|
|
141
187
|
render,
|
|
188
|
+
render_parts,
|
|
142
189
|
row,
|
|
143
190
|
run_rule_value,
|
|
144
191
|
runs_of,
|
|
192
|
+
CACHED_NOTE,
|
|
145
193
|
NO_TARGETS,
|
|
146
194
|
UNMEASURED,
|
|
147
195
|
UNREACHED,
|
|
@@ -159,7 +159,8 @@ function observe_call(
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// One transcript line, parsed once, as the things a reading is taken from: the calls it issued, the
|
|
162
|
-
//
|
|
162
|
+
// refusals the harness wrote back (failed results and Stop-hook blocks), and the context a hook
|
|
163
|
+
// attached.
|
|
163
164
|
interface DatedLine {
|
|
164
165
|
at_ms: number
|
|
165
166
|
message_id: string
|
|
@@ -183,6 +184,14 @@ function errors_of(parsed: TranscriptLine): Array<string> {
|
|
|
183
184
|
.filter((text) => text !== '')
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
// **A Stop-hook block is a refusal too** — of ending the turn — so its reason joins the
|
|
188
|
+
// tool results the harness wrote back as failures (joshuafolkken/kit#4978).
|
|
189
|
+
function refusals_of(parsed: TranscriptLine): Array<string> {
|
|
190
|
+
const errors = errors_of(parsed)
|
|
191
|
+
|
|
192
|
+
return parsed.stop_feedback === '' ? errors : [...errors, parsed.stop_feedback]
|
|
193
|
+
}
|
|
194
|
+
|
|
186
195
|
function dated_line(line: string): DatedLine | undefined {
|
|
187
196
|
const parsed = line === '' ? undefined : time_transcript_line.parse_line(line)
|
|
188
197
|
|
|
@@ -192,7 +201,7 @@ function dated_line(line: string): DatedLine | undefined {
|
|
|
192
201
|
at_ms: parsed.timestamp_ms,
|
|
193
202
|
message_id: parsed.message_id,
|
|
194
203
|
calls: calls_of(parsed),
|
|
195
|
-
errors:
|
|
204
|
+
errors: refusals_of(parsed),
|
|
196
205
|
contexts: hook_context_line.hook_contexts_of(line),
|
|
197
206
|
}
|
|
198
207
|
}
|
|
@@ -250,10 +259,14 @@ function turns_by_key(entries: ReadonlyArray<DatedLine>): Map<string, Array<Issu
|
|
|
250
259
|
// each of them; the file opens with its imports, so under this test nothing claims it. Position is
|
|
251
260
|
// what separates the two, so no count of how many signatures appear is needed — and a body naming
|
|
252
261
|
// several can no longer be attributed to any of them.
|
|
262
|
+
//
|
|
263
|
+
// **The harness's own label is not the body.** It writes a deny as `PreToolUse:<Tool> hook error: ⛔ …`,
|
|
264
|
+
// so the label is stripped before the opening is read; compared raw, no recorded refusal matched
|
|
265
|
+
// (joshuafolkken/kit#4961).
|
|
253
266
|
function refused_id(text: string): string | undefined {
|
|
254
|
-
const body =
|
|
267
|
+
const body = time_transcript_line.refusal_body(text)
|
|
255
268
|
const found = delivered_rules.MEASURED_RULES.find((rule) =>
|
|
256
|
-
[rule.reason, ...(rule.former_reasons ?? [])].some((reason) =>
|
|
269
|
+
[rule.reason, ...(rule.former_reasons ?? []), ...(rule.stop_reasons ?? [])].some((reason) =>
|
|
257
270
|
body.startsWith(reason.slice(0, REASON_SIGNATURE_LENGTH)),
|
|
258
271
|
),
|
|
259
272
|
)
|
|
@@ -35,7 +35,7 @@ import { shell_segments } from './shell-segments'
|
|
|
35
35
|
// one command cannot disagree: an opening subshell parenthesis, environment assignments, and the
|
|
36
36
|
// package-manager wrapper in either of its spellings. Left out, `JOSH_CI_TIMEOUT_SECONDS=600 pnpm
|
|
37
37
|
// josh git -y` and `(pnpm josh git -y)` are silently not the push step, and the run pays the tail the
|
|
38
|
-
// rule exists to stop. `g` is the alias `josh-
|
|
38
|
+
// rule exists to stop. `g` is the alias `josh-aliases.ts` resolves to `git`.
|
|
39
39
|
const SEGMENT_PREFIX = String.raw`(?:\(\s*)?(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*(?:npx\s+|pnpm\s+(?:exec\s+|run\s+)?)?`
|
|
40
40
|
// A closing parenthesis ends a word as a space does, so every boundary below admits it — without
|
|
41
41
|
// that, a subshell defeats the flag test even where it does not defeat the command test.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
2
|
+
import { bash_triggers } from './bash-triggers'
|
|
3
|
+
import { git_argv } from './git-argv'
|
|
4
|
+
import { shell_segments } from './shell-segments'
|
|
5
|
+
|
|
6
|
+
// The trigger and the delivered text behind the `sed-in-place` row of `delivered-rules.ts`: a `sed -i`
|
|
7
|
+
// that `file-edits.md`'s countable conditions do not clear (joshuafolkken/kit#5284).
|
|
8
|
+
//
|
|
9
|
+
// **The conditions are counts, so the guard counts them.** `file-edits.md` allows `sed -i` only for a
|
|
10
|
+
// single `s` command whose pattern carries no meta character and matches exactly once in each target
|
|
11
|
+
// file. Anything else — a meta character, a count other than one, a script that is not one `s` command —
|
|
12
|
+
// can rewrite a line the run never meant to touch, and `Edit` is the tool that cannot.
|
|
13
|
+
//
|
|
14
|
+
// It fires on every occurrence, for the reason `file-body.ts` does.
|
|
15
|
+
|
|
16
|
+
const SED = 'sed'
|
|
17
|
+
// `-i`, `-i.bak`, `-Ei`: a short cluster carrying the in-place switch; `--in-place[=suffix]` long.
|
|
18
|
+
const IN_PLACE_SHORT = /^-[A-Za-z]*i/u
|
|
19
|
+
const IN_PLACE_LONG = /^--in-place(?:=|$)/u
|
|
20
|
+
// A short cluster ending in the in-place switch: BSD reads its suffix from the next word.
|
|
21
|
+
const IN_PLACE_CLUSTER = /^-[A-Za-z]*i$/u
|
|
22
|
+
// `-E`, `-r`, `--regexp-extended`: the pattern is read as an extended regex, where `+ ? ( ) { } |`
|
|
23
|
+
// are meta characters too, so a plain-text count no longer says what sed matches.
|
|
24
|
+
const EXTENDED_SHORT = /^-[A-Za-z]*[Er]/u
|
|
25
|
+
const EXTENDED_LONG = '--regexp-extended'
|
|
26
|
+
// The `s` flags that keep the match plain text: none, or `g` (each line holds one match at most when
|
|
27
|
+
// the count is one). `I`, `w file`, a number and the rest change what or where sed writes.
|
|
28
|
+
const PLAIN_FLAGS: ReadonlySet<string> = new Set(['', 'g'])
|
|
29
|
+
const SCRIPT_FLAG = '-e'
|
|
30
|
+
const FLAG_PREFIX = '-'
|
|
31
|
+
const SURROUNDING_QUOTES = /^(['"])(.*)\1$/su
|
|
32
|
+
const QUOTED_BODY = 2
|
|
33
|
+
// `file-edits.md`'s meta characters: `/ & \ . * [ ] ^ $`.
|
|
34
|
+
const META_CHARACTER = /[/&\\.*[\]^$]/u
|
|
35
|
+
const SUBSTITUTE = 's'
|
|
36
|
+
const SUBSTITUTE_PARTS = 3
|
|
37
|
+
const ONE_MATCH = 1
|
|
38
|
+
|
|
39
|
+
interface SedCall {
|
|
40
|
+
scripts: Array<string>
|
|
41
|
+
files: Array<string>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function unquoted(word: string): string {
|
|
45
|
+
return SURROUNDING_QUOTES.exec(word)?.[QUOTED_BODY] ?? word
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function is_in_place_flag(word: string): boolean {
|
|
49
|
+
return IN_PLACE_LONG.test(word) || IN_PLACE_SHORT.test(word)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The words after the in-place flags: BSD's `-i ''` carries its suffix as the next (empty) word.
|
|
53
|
+
function without_suffix(words: ReadonlyArray<string>): Array<string> {
|
|
54
|
+
const index = words.findIndex((word) => IN_PLACE_CLUSTER.test(word))
|
|
55
|
+
|
|
56
|
+
if (index === -1 || words[index + 1] !== '') return [...words]
|
|
57
|
+
|
|
58
|
+
return words.toSpliced(index + 1, 1)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function sed_words(segment: string): Array<string> | undefined {
|
|
62
|
+
const words = git_argv.arguments_of(segment, SED)?.map((word) => unquoted(word))
|
|
63
|
+
|
|
64
|
+
return words?.some((word) => is_in_place_flag(word)) === true ? without_suffix(words) : undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function is_script_value(words: ReadonlyArray<string>, index: number): boolean {
|
|
68
|
+
return words[index - 1] === SCRIPT_FLAG
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The scripts (`-e <script>`, or the first operand without one) and the files of an in-place `sed`.
|
|
72
|
+
function sed_call(words: ReadonlyArray<string>): SedCall {
|
|
73
|
+
const scripts = words.filter((_word, index) => is_script_value(words, index))
|
|
74
|
+
const operands = words.filter(
|
|
75
|
+
(word, index) => !word.startsWith(FLAG_PREFIX) && !is_script_value(words, index),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if (scripts.length > 0) return { scripts, files: operands }
|
|
79
|
+
|
|
80
|
+
const [script, ...files] = operands
|
|
81
|
+
|
|
82
|
+
return { scripts: script === undefined ? [] : [script], files }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// An `s` command split on its delimiter into the pattern, the replacement and the flags; `undefined`
|
|
86
|
+
// for any other script.
|
|
87
|
+
function substitute_parts(script: string): Array<string> | undefined {
|
|
88
|
+
if (!script.startsWith(SUBSTITUTE)) return undefined
|
|
89
|
+
|
|
90
|
+
const parts = script.slice(SUBSTITUTE.length + 1).split(script.charAt(SUBSTITUTE.length))
|
|
91
|
+
|
|
92
|
+
return parts.length === SUBSTITUTE_PARTS ? parts : undefined
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// `file-edits.md` condition 2 reads both sides: no meta character in the pattern or the replacement,
|
|
96
|
+
// and no flag that turns the plain-text match into something else.
|
|
97
|
+
function has_plain_flags(parts: ReadonlyArray<string>): boolean {
|
|
98
|
+
return PLAIN_FLAGS.has(parts.at(-1) ?? '')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function is_plain(parts: ReadonlyArray<string>): boolean {
|
|
102
|
+
const [pattern = '', replacement = ''] = parts
|
|
103
|
+
|
|
104
|
+
return pattern !== '' && has_plain_flags(parts) && !META_CHARACTER.test(pattern + replacement)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function is_extended(words: ReadonlyArray<string>): boolean {
|
|
108
|
+
return words.some((word) => word === EXTENDED_LONG || EXTENDED_SHORT.test(word))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// The one script of a basic-regex in-place `sed`; `undefined` for several, none, or `-E`.
|
|
112
|
+
function single_script(words: ReadonlyArray<string>): string | undefined {
|
|
113
|
+
if (is_extended(words)) return undefined
|
|
114
|
+
|
|
115
|
+
const { scripts } = sed_call(words)
|
|
116
|
+
|
|
117
|
+
return scripts.length === 1 ? scripts[0] : undefined
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The pattern of a single plain `s` command; `undefined` for anything else.
|
|
121
|
+
function plain_pattern(words: ReadonlyArray<string>): string | undefined {
|
|
122
|
+
const script = single_script(words)
|
|
123
|
+
const parts = script === undefined ? undefined : substitute_parts(script)
|
|
124
|
+
|
|
125
|
+
return parts !== undefined && is_plain(parts) ? parts[0] : undefined
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function match_count(text: string, pattern: string): number {
|
|
129
|
+
return text.split(pattern).length - 1
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type ReadFile = (file_path: string) => string
|
|
133
|
+
|
|
134
|
+
// The trigger: an in-place `sed` the conditions do not clear in any of its target files.
|
|
135
|
+
function is_unsafe_segment(segment: string, read: ReadFile): boolean {
|
|
136
|
+
const words = sed_words(segment)
|
|
137
|
+
|
|
138
|
+
if (words === undefined) return false
|
|
139
|
+
|
|
140
|
+
const { files } = sed_call(words)
|
|
141
|
+
const pattern = plain_pattern(words)
|
|
142
|
+
|
|
143
|
+
if (pattern === undefined || files.length === 0) return true
|
|
144
|
+
|
|
145
|
+
return files.some((file) => match_count(read(file), pattern) !== ONE_MATCH)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function is_unsafe_sed(command: string, read: ReadFile = file_reader.read_file_or_empty): boolean {
|
|
149
|
+
return shell_segments.segments_of(command).some((segment) => is_unsafe_segment(segment, read))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The occasion the rule governs: any in-place `sed`.
|
|
153
|
+
function is_in_place_sed(command: string): boolean {
|
|
154
|
+
return shell_segments.segments_of(command).some((segment) => sed_words(segment) !== undefined)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Keeping the rule: an in-place `sed` whose script clears the static conditions. The per-file count
|
|
158
|
+
// is not re-read here, so a transcript is measured without the file as it stood then.
|
|
159
|
+
function keeps_plain_sed(command: string): boolean {
|
|
160
|
+
return shell_segments.segments_of(command).some((segment) => {
|
|
161
|
+
const words = sed_words(segment)
|
|
162
|
+
|
|
163
|
+
return words !== undefined && plain_pattern(words) !== undefined
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const SED_IN_PLACE_REASON =
|
|
168
|
+
'⛔ unsafe `sed -i`: its pattern or replacement carries a meta character (`/ & \\ . * [ ] ^ $`), ' +
|
|
169
|
+
'it is not one plain `s` command (a flag other than `g`, or `-E`), or it does not match exactly once in each target file, so it can rewrite a line you did ' +
|
|
170
|
+
'not mean to. Change the region with the Edit tool. This rule fires on every occurrence ' +
|
|
171
|
+
'(`prompts/collaboration-workflow/file-edits.md`).'
|
|
172
|
+
|
|
173
|
+
const ROW = {
|
|
174
|
+
id: 'sed-in-place',
|
|
175
|
+
is_trigger: bash_triggers.on_bash_command((command: string) => is_unsafe_sed(command)),
|
|
176
|
+
reason: SED_IN_PLACE_REASON,
|
|
177
|
+
decide: (): boolean => true,
|
|
178
|
+
keeps: bash_triggers.on_bash_command(keeps_plain_sed),
|
|
179
|
+
reaches: bash_triggers.on_bash_command(is_in_place_sed),
|
|
180
|
+
about: {
|
|
181
|
+
title: 'No `sed -i` its countable conditions do not clear',
|
|
182
|
+
topic: '`file-edits.md`',
|
|
183
|
+
fires:
|
|
184
|
+
'a `sed -i` whose script is not one `s` command, whose pattern or replacement has a meta character, or which does not match exactly once in a target file. Every occurrence',
|
|
185
|
+
quiet: 'not in place, or one plain `s` command matching once in each file',
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const sed_in_place = {
|
|
190
|
+
ROW,
|
|
191
|
+
is_in_place_sed,
|
|
192
|
+
is_unsafe_sed,
|
|
193
|
+
keeps_plain_sed,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export { sed_in_place }
|
|
@@ -20,8 +20,8 @@ const SEGMENT_PARTS = new RegExp(`(${SEGMENT_SEPARATOR.source})`, 'u')
|
|
|
20
20
|
// question is which program a segment launches, and `true | git push -f` or `x & git push -f`
|
|
21
21
|
// launches git all the same; a `--jq` fragment cut off by a `|` begins with no program word, so it
|
|
22
22
|
// launches nothing they look for. A `&` that belongs to a redirection (`2>&1`, `&>file`, `<&3`) does
|
|
23
|
-
// not cut
|
|
24
|
-
const COMMAND_SEPARATOR = /&&|\|\||;|\n
|
|
23
|
+
// not cut, nor does the `|` of the clobber redirect `>|`.
|
|
24
|
+
const COMMAND_SEPARATOR = /&&|\|\||;|\n|(?<!>)\|&?|(?<![<>])&(?!>)/u
|
|
25
25
|
|
|
26
26
|
/** The line's commands, trimmed. Empty segments are kept: a caller anchoring at `^` rejects them. */
|
|
27
27
|
function segments_of(command: string): Array<string> {
|
|
@@ -54,6 +54,13 @@ function quote_aware_commands_of(command: string): Array<string> {
|
|
|
54
54
|
return starts.map((start, index) => command.slice(start, stops[index] ?? command.length).trim())
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
const LINE_CONTINUATION = /\\\n/gu
|
|
58
|
+
|
|
59
|
+
/** The line with each backslash-newline joined, so a continued call stays one command at a `\n` cut. */
|
|
60
|
+
function joined_continuations(command: string): string {
|
|
61
|
+
return command.replaceAll(LINE_CONTINUATION, ' ')
|
|
62
|
+
}
|
|
63
|
+
|
|
57
64
|
/** The same cut, untrimmed and with each separator kept between its commands, so the parts join back to the line. */
|
|
58
65
|
function parts_of(command: string): Array<string> {
|
|
59
66
|
return command.split(SEGMENT_PARTS)
|
|
@@ -91,6 +98,7 @@ const shell_segments = {
|
|
|
91
98
|
commands_of,
|
|
92
99
|
first_of,
|
|
93
100
|
is_josh_command,
|
|
101
|
+
joined_continuations,
|
|
94
102
|
parts_of,
|
|
95
103
|
quote_aware_commands_of,
|
|
96
104
|
segments_of,
|
|
@@ -13,7 +13,7 @@ const ROWS: ReadonlyArray<StopRule> = [
|
|
|
13
13
|
id: 'stop-notify',
|
|
14
14
|
about: {
|
|
15
15
|
title: 'Stop notification',
|
|
16
|
-
topic: '`
|
|
16
|
+
topic: '`rule-delivery.md` → "stop-notify"',
|
|
17
17
|
entry: STOP_GUARD,
|
|
18
18
|
fires:
|
|
19
19
|
'a stop with the work tree held and no `confirmation` notify that turn. Sends the notify itself; blocks only when the send fails',
|
|
@@ -32,18 +32,6 @@ const ROWS: ReadonlyArray<StopRule> = [
|
|
|
32
32
|
'a dirty tree (a `halfrun` stop before the commit, a `needs-human-review` stop) or already released',
|
|
33
33
|
},
|
|
34
34
|
},
|
|
35
|
-
{
|
|
36
|
-
id: 'issue-citation',
|
|
37
|
-
about: {
|
|
38
|
-
title: 'Issue citation format',
|
|
39
|
-
topic: '`prompts/collaboration-workflow/issue-citation.md`',
|
|
40
|
-
entry: STOP_GUARD,
|
|
41
|
-
fires:
|
|
42
|
-
'a bare `#N` in the prose of the last reply. Prints the citation lines itself; blocks only when a number cannot be cited',
|
|
43
|
-
quiet:
|
|
44
|
-
'link form, inside code or a quote, `#N` right after `PR`, or a GitHub-facing artifact',
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
35
|
{
|
|
48
36
|
id: 'filing-offer',
|
|
49
37
|
about: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { hook_decision } from '#scripts/josh/hook-decision'
|
|
2
2
|
import { json_value } from '#scripts/lib/json-value'
|
|
3
3
|
import { z } from 'zod'
|
|
4
|
+
import { concise_reply } from './concise-reply'
|
|
4
5
|
import { filing_offer } from './filing-offer'
|
|
5
|
-
import { issue_citation } from './issue-citation'
|
|
6
6
|
import { lane_background } from './lane-background'
|
|
7
7
|
import { reply_language } from './reply-language'
|
|
8
8
|
import { stop_decision, type StopDecision } from './stop-decision'
|
|
@@ -22,7 +22,7 @@ import { stop_decision, type StopDecision } from './stop-decision'
|
|
|
22
22
|
// `pretool-guard` is one — not a second copy of the plumbing.
|
|
23
23
|
//
|
|
24
24
|
// **A rule whose answer is fixed acts; only a rule that needs judgement refuses** (joshuafolkken/kit#3919).
|
|
25
|
-
// The stop notification
|
|
25
|
+
// The stop notification and the hold release are each one mechanical operation the
|
|
26
26
|
// hook can run itself, so they come back as `actions` and the stop goes through: a refusal is a whole
|
|
27
27
|
// model turn spent re-reading the context to run a command the hook already knew. `stop-actions.ts`
|
|
28
28
|
// runs them, and only an action that fails falls back to its refusal. The rest — the reply's language,
|
|
@@ -34,7 +34,7 @@ const SWITCH_ENV_KEY = 'JOSH_STOP_GUARD'
|
|
|
34
34
|
const BLOCK_DECISION = 'block'
|
|
35
35
|
|
|
36
36
|
// Only the fields the stop rules read. `last_assistant_message` is the turn's session-facing text —
|
|
37
|
-
// Claude Code hands it directly so the transcript's async lag is never in the
|
|
37
|
+
// Claude Code hands it directly so the transcript's async lag is never in the rules' path.
|
|
38
38
|
const stop_payload_schema = z.object({
|
|
39
39
|
transcript_path: z.string().min(1),
|
|
40
40
|
stop_hook_active: z.boolean().nullish(),
|
|
@@ -64,8 +64,8 @@ interface StopContext {
|
|
|
64
64
|
notified: boolean
|
|
65
65
|
// The turn's session-facing reply text.
|
|
66
66
|
message: string
|
|
67
|
-
// The person's last prompt —
|
|
68
|
-
prompt: string
|
|
67
|
+
// The person's last prompt on the tail — `undefined` when none is on it (`last_prompt.person_prompt`).
|
|
68
|
+
prompt: string | undefined
|
|
69
69
|
// This stop already forced a continuation earlier in the prompt — the loop-breaker Claude Code
|
|
70
70
|
// documents, so a run that will not comply is not blocked forever.
|
|
71
71
|
stop_hook_active: boolean
|
|
@@ -74,7 +74,7 @@ interface StopContext {
|
|
|
74
74
|
// the cut holding the tree by design and hands off to a fresh process (`pre-gate-cut.md`), so that
|
|
75
75
|
// turn-end is an automatic continuation, not the person-waiting pause the block rules are for.
|
|
76
76
|
cut_pending: boolean
|
|
77
|
-
// An Issue filing no guard refused is on this run's transcript tail — `
|
|
77
|
+
// An Issue filing no guard refused is on this run's transcript tail — `turn_filings`' count.
|
|
78
78
|
filed: boolean
|
|
79
79
|
// The owner of the repository the session runs in, or `undefined` when it cannot be read.
|
|
80
80
|
session_owner: string | undefined
|
|
@@ -91,6 +91,9 @@ interface StopContext {
|
|
|
91
91
|
// A backgrounded subagent this run launched has not finished — `lane_background.pending_agent_ids`.
|
|
92
92
|
// Apart from `background_pending`, since only a subagent is sure to end.
|
|
93
93
|
agent_pending: boolean
|
|
94
|
+
// A backgrounded command this run launched has not finished — `lane_background.pending_command_ids`.
|
|
95
|
+
// Apart from `agent_pending`, since the two are waited on differently.
|
|
96
|
+
command_pending: boolean
|
|
94
97
|
// This lane child handed its region to a running detached ship — `lane_handoff.is_handed_off`.
|
|
95
98
|
// The supervisor notifies on its own outcome, so nobody waits on this stop.
|
|
96
99
|
handed_off: boolean
|
|
@@ -108,7 +111,6 @@ interface StopContext {
|
|
|
108
111
|
type StopAction =
|
|
109
112
|
| { kind: 'notify'; issue: string; message: string; decision: StopDecision }
|
|
110
113
|
| { kind: 'release'; issue: string }
|
|
111
|
-
| { kind: 'cite'; references: ReadonlyArray<string> }
|
|
112
114
|
|
|
113
115
|
// `reason` refuses the stop; otherwise `actions` run, and `blocks_on_failure` says whether a failed one
|
|
114
116
|
// falls back to its refusal — never past the loop-breaker.
|
|
@@ -128,8 +130,8 @@ const STOP_NOTIFY_REASON =
|
|
|
128
130
|
'⛔ mid-workflow stop notification: this working tree is still held by a run and no `confirmation` ' +
|
|
129
131
|
'Telegram was sent this turn. Send ' +
|
|
130
132
|
'`pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\' ' +
|
|
131
|
-
'--recommended "<choice>" --alternative "<choice>"` (`
|
|
132
|
-
'
|
|
133
|
+
'--recommended "<choice>" --alternative "<choice>"` (`rule-delivery.md` → "stop-notify"), ' +
|
|
134
|
+
'then confirm in one line that it was sent — do not repeat your previous reply.'
|
|
133
135
|
|
|
134
136
|
// **A clean tree that still holds is a tree the next run will trample.** `working-tree-hold.md`: a stop that
|
|
135
137
|
// leaves the tree clean releases the hold; a `halfrun` pre-commit stop and a `needs-human-review` stop
|
|
@@ -141,28 +143,6 @@ const HOLD_RELEASE_REASON =
|
|
|
141
143
|
'`working-tree-hold.md`), then confirm in one line that it was released — do not repeat your ' +
|
|
142
144
|
'previous reply.'
|
|
143
145
|
|
|
144
|
-
// **The fallback when the hook could not print the citation lines itself**. The bare `#N` is already
|
|
145
|
-
// on screen and the hook cannot unsay it, so the reason does the one thing that helps the *next* reply:
|
|
146
|
-
// it names the numbers it detected and hands over the exact `issue:cite` call that prints the
|
|
147
|
-
// paste-ready lines, then asks for the corrected citation lines alone — not the whole reply reissued.
|
|
148
|
-
// Reprinting the whole reply is what made the correction read as a duplicate;
|
|
149
|
-
// the bare copy already scrolled past stays, and the fix follows it as a short correction. The rule
|
|
150
|
-
// itself is not restated — it is resident in `CLAUDE.md` — only pointed at.
|
|
151
|
-
const ISSUE_CITATION_REASON =
|
|
152
|
-
'Session-facing output cites an Issue as a number-link so the reader can click through and see ' +
|
|
153
|
-
'which repository it is (`CLAUDE.md`, `prompts/collaboration-workflow/issue-citation.md`).'
|
|
154
|
-
|
|
155
|
-
function build_citation_reason(references: ReadonlyArray<string>): string {
|
|
156
|
-
const numbers = references.join(', ')
|
|
157
|
-
const command = `pnpm josh issue:cite ${issue_citation.cite_arguments(references).join(' ')}`
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
`⛔ issue citation: your reply names an Issue as a bare \`#N\` (${numbers}). Run \`${command}\` ` +
|
|
161
|
-
`to print the paste-ready citation lines, then print the corrected citation lines alone — do not ` +
|
|
162
|
-
`repeat your previous reply. ${ISSUE_CITATION_REASON}`
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
146
|
// **An offer to file in an unattended run is a Tier A filing deferred to nobody**.
|
|
167
147
|
// The judgement is already made, so the reason hands over the filing chain rather than a question; the
|
|
168
148
|
// rule itself is resident in `observation-filing.md` and only pointed at. **An interactive session asks
|
|
@@ -249,6 +229,13 @@ function language_reason(context: StopContext): string | undefined {
|
|
|
249
229
|
return build_language_reason(context.session_lang)
|
|
250
230
|
}
|
|
251
231
|
|
|
232
|
+
// **A reply's narrative over the limit is sent back once to be shortened** — `concise-reply.ts`.
|
|
233
|
+
function concise_reason(context: StopContext): string | undefined {
|
|
234
|
+
if (!concise_reply.is_over(context.message, context.prompt)) return undefined
|
|
235
|
+
|
|
236
|
+
return concise_reply.build_reason(context.message)
|
|
237
|
+
}
|
|
238
|
+
|
|
252
239
|
// The sessions no person answers: one kit launched under `claude -p` (a lane child, a woken session), a
|
|
253
240
|
// dispatched lane child, or the `backlogrun` parent the user handed the backlog to.
|
|
254
241
|
function is_unattended(context: StopContext): boolean {
|
|
@@ -295,24 +282,16 @@ function needs_release(context: StopContext): boolean {
|
|
|
295
282
|
return is_held_idle(context) && context.tree_clean && !context.prrun_stopped
|
|
296
283
|
}
|
|
297
284
|
|
|
298
|
-
// The
|
|
299
|
-
// its text lands in a stream-json log, so
|
|
300
|
-
//
|
|
301
|
-
// to the same rules by their own guards, not here.
|
|
302
|
-
function bare_references(context: StopContext): ReadonlyArray<string> {
|
|
303
|
-
if (context.headless_agent) return []
|
|
304
|
-
|
|
305
|
-
return issue_citation.unquoted_references(context.message, context.prompt)
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// The two rules that read the reply and need the model, the filing offer first. A drifted reply
|
|
285
|
+
// The rules that read the reply and need the model: the filing offer, then the language, then the
|
|
286
|
+
// length. **A headless agent's reply has no reader**: its text lands in a stream-json log, so
|
|
287
|
+
// correcting its language costs a turn and reaches nobody. A drifted reply
|
|
309
288
|
// refused for another reason is not rewritten, but the drift still ends: `block_envelope` appends the
|
|
310
289
|
// session-language note to every refusal, so the turn it continues is written in the session language.
|
|
311
290
|
function reply_reason(context: StopContext): string | undefined {
|
|
312
291
|
if (needs_filing(context)) return FILING_OFFER_REASON
|
|
313
292
|
if (context.headless_agent) return undefined
|
|
314
293
|
|
|
315
|
-
return language_reason(context)
|
|
294
|
+
return language_reason(context) ?? concise_reason(context)
|
|
316
295
|
}
|
|
317
296
|
|
|
318
297
|
// Sent back once; past the loop-breaker the run has said nothing is awaited, and nothing is sent.
|
|
@@ -342,11 +321,19 @@ function needs_background_wait(context: StopContext): boolean {
|
|
|
342
321
|
return context.lane_child && context.background_pending
|
|
343
322
|
}
|
|
344
323
|
|
|
324
|
+
// **Only subagents left take their own wait**: their notice comes in when a tool call ends, while a
|
|
325
|
+
// command is waited on through the detached ship.
|
|
326
|
+
function background_wait_reason(context: StopContext): string {
|
|
327
|
+
if (context.command_pending) return lane_background.LANE_BACKGROUND_STOP_REASON
|
|
328
|
+
|
|
329
|
+
return lane_background.LANE_AGENT_STOP_REASON
|
|
330
|
+
}
|
|
331
|
+
|
|
345
332
|
// The two lane rules that mean the run is not pausing at all — ahead of every action, since the notify
|
|
346
333
|
// would announce a pause the run has no reason to take.
|
|
347
334
|
function lane_reason(context: StopContext): string | undefined {
|
|
348
335
|
if (needs_index_route(context)) return LANE_INDEX_REASON
|
|
349
|
-
if (needs_background_wait(context)) return
|
|
336
|
+
if (needs_background_wait(context)) return background_wait_reason(context)
|
|
350
337
|
|
|
351
338
|
return undefined
|
|
352
339
|
}
|
|
@@ -382,14 +369,6 @@ function hold_actions(context: StopContext): Array<StopAction> {
|
|
|
382
369
|
return needs_release(context) ? [...notify, { kind: 'release', issue }] : notify
|
|
383
370
|
}
|
|
384
371
|
|
|
385
|
-
// The actions in the order they run: the notify ahead of the release that ends the hold it reports.
|
|
386
|
-
function stop_actions(context: StopContext): ReadonlyArray<StopAction> {
|
|
387
|
-
const references = bare_references(context)
|
|
388
|
-
const cite: Array<StopAction> = references.length > 0 ? [{ kind: 'cite', references }] : []
|
|
389
|
-
|
|
390
|
-
return [...hold_actions(context), ...cite]
|
|
391
|
-
}
|
|
392
|
-
|
|
393
372
|
// First-wins across the refusals. `stop_hook_active` is the loop-breaker, so a run that already got one
|
|
394
373
|
// continuation this prompt may stop; the lane rules go first, then the filing offer, then the language.
|
|
395
374
|
//
|
|
@@ -418,7 +397,7 @@ function stop_outcome(context: StopContext): StopOutcome {
|
|
|
418
397
|
const reason = block_reason(context)
|
|
419
398
|
if (!runs_actions(context, reason)) return { ...NO_OUTCOME, reason }
|
|
420
399
|
|
|
421
|
-
const actions =
|
|
400
|
+
const actions = hold_actions(context)
|
|
422
401
|
if (actions.length === 0) return NO_OUTCOME
|
|
423
402
|
|
|
424
403
|
return { reason, actions, blocks_on_failure: !context.stop_hook_active }
|
|
@@ -426,10 +405,7 @@ function stop_outcome(context: StopContext): StopOutcome {
|
|
|
426
405
|
|
|
427
406
|
// The refusal an action falls back to when the hook could not run it.
|
|
428
407
|
function fallback_reason(action: StopAction): string {
|
|
429
|
-
|
|
430
|
-
if (action.kind === 'release') return HOLD_RELEASE_REASON
|
|
431
|
-
|
|
432
|
-
return build_citation_reason(action.references)
|
|
408
|
+
return action.kind === 'notify' ? STOP_NOTIFY_REASON : HOLD_RELEASE_REASON
|
|
433
409
|
}
|
|
434
410
|
|
|
435
411
|
// The documented shape a `Stop` hook blocks with: `reason` is fed back to Claude, which then continues
|
|
@@ -460,7 +436,6 @@ const stop_rules = {
|
|
|
460
436
|
HEADLESS_REFUSAL_CAP,
|
|
461
437
|
HEADLESS_WAIT_REASON,
|
|
462
438
|
HOLD_RELEASE_REASON,
|
|
463
|
-
ISSUE_CITATION_REASON,
|
|
464
439
|
LANE_INDEX_REASON,
|
|
465
440
|
NO_OUTCOME,
|
|
466
441
|
STOP_NOTIFY_REASON,
|
|
@@ -44,7 +44,7 @@ const ROW = {
|
|
|
44
44
|
reason: REASON,
|
|
45
45
|
about: {
|
|
46
46
|
title: 'Test declaration',
|
|
47
|
-
topic: '`
|
|
47
|
+
topic: '`testing-guide.md` → "0. Per-Requirement Test Coverage (MANDATORY)"',
|
|
48
48
|
fires: '`pnpm josh git -y` (`josh g`) while `pnpm josh test:declared` answers `required`',
|
|
49
49
|
quiet: '`exempt` (non-runtime only) or `satisfied` (tests included)',
|
|
50
50
|
},
|