@joshuafolkken/kit 1.1178.0 → 1.1240.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/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +7 -3
- package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
- package/.claude/skills/workflow-commands/chain-rule.md +1 -1
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +10 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -1
- package/.claude/skills/workflow-commands/latest-gate.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
- package/.claude/skills/workflow-commands/prerequisite.md +2 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
- package/.codex/hooks.json +2 -2
- package/.cursorrules +6 -41
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/auto-tag.yml +3 -4
- package/.github/workflows/ci.yml +32 -31
- package/.github/workflows/dependabot-auto-merge.yml +13 -6
- package/.github/workflows/publish.yml +50 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +44 -38
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
- package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
- package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
- package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
- package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
- package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
- package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
- package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +203 -179
- package/dist/hooks/investigation-guard.js +6 -5
- package/dist/hooks/pretool-guard.js +12 -10
- package/dist/hooks/session-lang.js +2 -4
- package/dist/hooks/stop-guard.js +66 -72
- package/dist/josh.js +1088 -78
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-template.md +1 -0
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +16 -5
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-scope.ts +1 -1
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +3 -1
- package/scripts/build/build-commands.ts +66 -0
- package/scripts/build/build-hooks.ts +2 -1
- package/scripts/build/build-library.ts +12 -3
- package/scripts/build/build.ts +70 -0
- package/scripts/build/local-bin.ts +3 -13
- package/scripts/delegation/investigation-reads.ts +3 -1
- package/scripts/discovery/repo-origin.ts +5 -5
- package/scripts/doctor/doctor-consumer.ts +2 -2
- package/scripts/doctor/doctor-io.ts +2 -3
- package/scripts/document/capped-print-part.ts +30 -0
- package/scripts/document/capped-print.ts +3 -4
- package/scripts/document/document-byte-budget.ts +11 -2
- package/scripts/document/document-scan.ts +117 -0
- package/scripts/document/document-section.ts +143 -16
- package/scripts/document/entry-read-budget.ts +5 -3
- package/scripts/document/entry-read-set.ts +10 -89
- package/scripts/document/read-set-cost.ts +98 -0
- package/scripts/epic/epic-add-body.ts +1 -0
- package/scripts/epic/epic-audit-checks.ts +1 -1
- package/scripts/epic/epic-audit-rationale.ts +4 -3
- package/scripts/epic/epic-bundle-cli.ts +14 -3
- package/scripts/epic/epic-bundle-poll.ts +41 -0
- package/scripts/epic/epic-busy.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +3 -2
- package/scripts/epic/epic-classify.ts +1 -1
- package/scripts/epic/epic-cli-argv.ts +78 -46
- package/scripts/epic/epic-cli-remove.ts +11 -19
- package/scripts/epic/epic-cli.ts +74 -73
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +5 -5
- package/scripts/epic/epic-graph.ts +1 -1
- package/scripts/epic/epic-index.ts +1 -1
- package/scripts/epic/epic-issue.ts +2 -6
- package/scripts/epic/epic-lane-offer.ts +2 -2
- package/scripts/epic/epic-next.ts +29 -42
- package/scripts/epic/epic-solo-stale.ts +1 -1
- package/scripts/epic/epic-triage.ts +8 -4
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/followup/git-followup-finish.ts +2 -2
- package/scripts/followup/git-followup-label.ts +2 -2
- package/scripts/followup/git-followup-merged.ts +2 -2
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +4 -129
- package/scripts/gate/core-admission.ts +162 -0
- package/scripts/gate/core-budget.ts +190 -68
- package/scripts/gate/gate-plan.ts +80 -7
- package/scripts/gate/gate-report.ts +28 -9
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/machine-capacity.ts +175 -0
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +34 -10
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-pr-read.ts +64 -19
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr.ts +5 -1
- package/scripts/gh/git-pr-ai-review.ts +1 -1
- package/scripts/gh/git-pr-checks-eval.ts +1 -3
- package/scripts/gh/git-pr-checks-parse.ts +1 -1
- package/scripts/gh/git-pr-checks-watch.ts +6 -5
- package/scripts/gh/git-pr-checks.ts +7 -5
- package/scripts/gh/git-pr-coderabbit.ts +1 -1
- package/scripts/gh/git-pr-error.ts +1 -3
- package/scripts/gh/git-pr.ts +9 -13
- package/scripts/gh/github-issue-url.ts +2 -2
- package/scripts/git/constants.ts +2 -6
- package/scripts/git/git-command.ts +20 -218
- package/scripts/git/git-diff-reads.ts +225 -0
- package/scripts/git/git-error.ts +4 -12
- package/scripts/git/git-pre-push-hook.ts +139 -0
- package/scripts/git/git-prompt.ts +2 -6
- package/scripts/git/git-spawn-sync.ts +3 -1
- package/scripts/git/git-spawn.ts +1 -1
- package/scripts/git/git-staging.ts +4 -6
- package/scripts/git/git-status.ts +3 -9
- package/scripts/hooks/codex-hook-adapter.ts +1 -1
- package/scripts/hooks/edited-cspell.ts +2 -4
- package/scripts/hooks/format-edited-file.ts +3 -12
- package/scripts/hooks/pre-push-unit.ts +31 -9
- package/scripts/hooks/reserved-run.ts +2 -2
- package/scripts/hooks/run-hook.sh +5 -4
- package/scripts/hooks/stop-guard.ts +5 -3
- package/scripts/init/init-actions.ts +0 -5
- package/scripts/init/init-ai-copy.ts +13 -2
- package/scripts/init/init-logic-json-merge.ts +19 -0
- package/scripts/init/init-logic-secretlint.ts +7 -7
- package/scripts/init/init-logic-workspace.ts +41 -2
- package/scripts/init/init-logic.ts +16 -26
- package/scripts/init/init.ts +10 -16
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/package-manager-pin.ts +35 -0
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +32 -7
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +72 -22
- package/scripts/issue/issue-file.ts +29 -1
- package/scripts/issue/issue-labels.ts +13 -0
- package/scripts/issue/issue-release-cli.ts +155 -0
- package/scripts/issue/issue-release.ts +58 -0
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +14 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +18 -0
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-issue.ts +8 -1
- package/scripts/josh/josh-commands-lane.ts +24 -1
- package/scripts/josh/josh-commands-maintenance.ts +6 -6
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +34 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +12 -4
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +15 -1
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +44 -30
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-open.ts +5 -5
- package/scripts/lane/lane-paths.ts +13 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-registry.ts +11 -0
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-stray.ts +140 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/release/github-release-environment.ts +1 -0
- package/scripts/release/github-release-runs.ts +115 -0
- package/scripts/release/github-release.ts +13 -67
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +1 -1
- package/scripts/rules/protected-files.ts +59 -4
- package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
- package/scripts/rules/shell-body-trigger.ts +1 -1
- package/scripts/rules/stop-rules.ts +18 -7
- package/scripts/rules/test-declared-commit.ts +4 -4
- package/scripts/rules/wip-cap.ts +63 -0
- package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
- package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
- package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
- package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
- package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
- package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +7 -4
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
- package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
- package/scripts/run/ship/run-ship-lock.ts +50 -0
- package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
- package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
- package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
- package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
- package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
- package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
- package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
- package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
- package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/unit-worker-share.ts +1 -0
- package/scripts/time-runtime/time-background.ts +5 -1
- package/scripts/time-runtime/time-instant.ts +3 -1
- package/scripts/time-runtime/time-spans.ts +6 -139
- package/scripts/time-runtime/time-tool-call.ts +160 -0
- package/scripts/version/effective-upstream.ts +2 -20
- package/scripts/version/latest-corepack.ts +124 -93
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +6 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
- /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
- /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
- /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
- /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
- /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
- /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
- /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
- /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
- /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
- /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
- /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
- /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
- /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
- /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
- /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
- /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
- /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
duplicate_read_outcome
|
|
3
|
+
} from "./chunk-QVSWU24O.js";
|
|
1
4
|
import {
|
|
2
5
|
ALREADY_DONE_LABEL,
|
|
3
6
|
CONTEXT_CUT_THRESHOLD,
|
|
@@ -8,7 +11,6 @@ import {
|
|
|
8
11
|
agent_role_profile,
|
|
9
12
|
bash_triggers,
|
|
10
13
|
cli_flags,
|
|
11
|
-
error_text,
|
|
12
14
|
filing_cap,
|
|
13
15
|
git_command,
|
|
14
16
|
git_common_directory,
|
|
@@ -31,35 +33,35 @@ import {
|
|
|
31
33
|
run_cut_handoff,
|
|
32
34
|
run_headless,
|
|
33
35
|
run_hold,
|
|
34
|
-
|
|
36
|
+
run_tail_rule,
|
|
35
37
|
shell_segments,
|
|
36
38
|
tail_commands
|
|
37
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-KHX4U3BG.js";
|
|
38
40
|
import {
|
|
41
|
+
ancestor_directories,
|
|
39
42
|
find_local_bin_upwards
|
|
40
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-JBBMAPC4.js";
|
|
44
|
+
import {
|
|
45
|
+
error_text
|
|
46
|
+
} from "./chunk-CDY4NFQG.js";
|
|
41
47
|
import {
|
|
42
48
|
batch_outcome
|
|
43
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-F3ELQ2AF.js";
|
|
44
50
|
import {
|
|
45
51
|
investigation_refusal
|
|
46
|
-
} from "./chunk-
|
|
47
|
-
import {
|
|
48
|
-
duplicate_read_outcome
|
|
49
|
-
} from "./chunk-DO6NWWHU.js";
|
|
52
|
+
} from "./chunk-KRBERBBI.js";
|
|
50
53
|
import {
|
|
51
54
|
investigation_reads
|
|
52
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-XZYKEM6J.js";
|
|
53
56
|
import {
|
|
54
57
|
lane_guard_policy
|
|
55
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-GPTFMK5E.js";
|
|
56
59
|
import {
|
|
57
60
|
hook_decision,
|
|
58
61
|
lane_child_marker,
|
|
59
62
|
run_issue_number
|
|
60
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-ZQGD4GBY.js";
|
|
61
64
|
import {
|
|
62
|
-
PLATFORM_TEMP_ROOT,
|
|
63
65
|
cost_attribute,
|
|
64
66
|
cost_blocks,
|
|
65
67
|
cost_transcript,
|
|
@@ -68,13 +70,18 @@ import {
|
|
|
68
70
|
json_value,
|
|
69
71
|
lane_paths,
|
|
70
72
|
open_issue_schema,
|
|
71
|
-
stamp_file,
|
|
72
73
|
time_batch_guard,
|
|
74
|
+
time_density_hook,
|
|
73
75
|
time_shell
|
|
74
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-WK2XSHSI.js";
|
|
77
|
+
import {
|
|
78
|
+
PLATFORM_TEMP_ROOT,
|
|
79
|
+
PROBE_TIMEOUT_MS,
|
|
80
|
+
stamp_file
|
|
81
|
+
} from "./chunk-ELCN5LCH.js";
|
|
75
82
|
|
|
76
83
|
// scripts/rules/direct-filing.ts
|
|
77
|
-
var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands-
|
|
84
|
+
var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands-backlog.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
|
|
78
85
|
function is_any_filing(command) {
|
|
79
86
|
return bash_triggers.is_direct_filing(command) || bash_triggers.is_issue_filing(command);
|
|
80
87
|
}
|
|
@@ -88,7 +95,7 @@ var ROW = {
|
|
|
88
95
|
};
|
|
89
96
|
var direct_filing = { DIRECT_FILING_REASON, ROW };
|
|
90
97
|
|
|
91
|
-
// scripts/run/run-progress.ts
|
|
98
|
+
// scripts/run/progress/run-progress.ts
|
|
92
99
|
var DEFAULT_INTERVAL_MINUTES = 20;
|
|
93
100
|
var INTERVAL_KEY = "JOSH_PROGRESS_INTERVAL_MINUTES";
|
|
94
101
|
var MS_PER_MINUTE = 6e4;
|
|
@@ -207,7 +214,7 @@ var run_progress = {
|
|
|
207
214
|
observation_key
|
|
208
215
|
};
|
|
209
216
|
|
|
210
|
-
// scripts/run/run-progress-clock.ts
|
|
217
|
+
// scripts/run/progress/run-progress-clock.ts
|
|
211
218
|
import { z } from "zod";
|
|
212
219
|
var PROGRESS_PREFIX = "josh-run-progress-";
|
|
213
220
|
var LIFE_PREFIX = "josh-run-progress-life-";
|
|
@@ -333,24 +340,14 @@ var run_progress_clock = {
|
|
|
333
340
|
stamp_target_of
|
|
334
341
|
};
|
|
335
342
|
|
|
336
|
-
// scripts/run/run-progress-config.ts
|
|
343
|
+
// scripts/run/progress/run-progress-config.ts
|
|
337
344
|
import { readFileSync } from "node:fs";
|
|
338
345
|
import path from "node:path";
|
|
339
346
|
import { z as z2 } from "zod";
|
|
340
347
|
var PACKAGE_FILE = "package.json";
|
|
341
|
-
var MAX_DEPTH = 10;
|
|
342
348
|
var package_schema = z2.object({
|
|
343
349
|
josh: z2.object({ progress_interval_minutes: z2.number().positive() }).optional()
|
|
344
350
|
});
|
|
345
|
-
function ancestors_of(start) {
|
|
346
|
-
const directories = [];
|
|
347
|
-
let current = path.resolve(start);
|
|
348
|
-
while (directories.length < MAX_DEPTH && !directories.includes(current)) {
|
|
349
|
-
directories.push(current);
|
|
350
|
-
current = path.dirname(current);
|
|
351
|
-
}
|
|
352
|
-
return directories;
|
|
353
|
-
}
|
|
354
351
|
function read_declared_minutes(directory) {
|
|
355
352
|
try {
|
|
356
353
|
const raw = readFileSync(path.join(directory, PACKAGE_FILE), "utf8");
|
|
@@ -361,7 +358,7 @@ function read_declared_minutes(directory) {
|
|
|
361
358
|
}
|
|
362
359
|
}
|
|
363
360
|
function read_package_minutes(start) {
|
|
364
|
-
for (const directory of
|
|
361
|
+
for (const directory of ancestor_directories.list(start)) {
|
|
365
362
|
const minutes = read_declared_minutes(directory);
|
|
366
363
|
if (minutes !== void 0) return minutes;
|
|
367
364
|
}
|
|
@@ -551,48 +548,6 @@ var file_body = {
|
|
|
551
548
|
carries_a_file_body
|
|
552
549
|
};
|
|
553
550
|
|
|
554
|
-
// scripts/rules/gh-api.ts
|
|
555
|
-
var GH_FLAGS = String.raw`(?:-{1,2}[\w-]+(?:[= ][^\s]+)?\s+)*`;
|
|
556
|
-
var GH_API_COMMAND = new RegExp(String.raw`^gh\s+${GH_FLAGS}api\s`, "u");
|
|
557
|
-
var API_METHOD = /(?:^|\s)(?:--method|-X)[= ]([A-Za-z]+)/u;
|
|
558
|
-
var API_FIELD = /(?:^|\s)(?:--raw-field|--field|--input|-F|-f)(?:[= ]|$)/u;
|
|
559
|
-
var READ_METHOD = "GET";
|
|
560
|
-
var REPO_PATH = /repos\/([^/\s'"?]+)\/([^/\s'"?]+)/u;
|
|
561
|
-
var OWNER_GROUP = 1;
|
|
562
|
-
var REPO_GROUP = 2;
|
|
563
|
-
var OWNER_PLACEHOLDERS = /* @__PURE__ */ new Set(["{owner}", ":owner"]);
|
|
564
|
-
function is_gh_api(segment) {
|
|
565
|
-
return GH_API_COMMAND.test(segment);
|
|
566
|
-
}
|
|
567
|
-
function method_of(segment) {
|
|
568
|
-
return API_METHOD.exec(segment)?.[1]?.toUpperCase();
|
|
569
|
-
}
|
|
570
|
-
function is_read(segment) {
|
|
571
|
-
const method = method_of(segment);
|
|
572
|
-
if (method !== void 0) return method.toUpperCase() === READ_METHOD;
|
|
573
|
-
return !API_FIELD.test(segment);
|
|
574
|
-
}
|
|
575
|
-
function is_write(segment) {
|
|
576
|
-
return !is_read(segment);
|
|
577
|
-
}
|
|
578
|
-
function repo_target(segment) {
|
|
579
|
-
const match = REPO_PATH.exec(segment);
|
|
580
|
-
if (match === null) return void 0;
|
|
581
|
-
return { owner: match[OWNER_GROUP] ?? "", repo: match[REPO_GROUP] ?? "" };
|
|
582
|
-
}
|
|
583
|
-
function is_owner_placeholder(owner) {
|
|
584
|
-
return OWNER_PLACEHOLDERS.has(owner);
|
|
585
|
-
}
|
|
586
|
-
var gh_api = {
|
|
587
|
-
GH_FLAGS,
|
|
588
|
-
is_gh_api,
|
|
589
|
-
is_owner_placeholder,
|
|
590
|
-
is_read,
|
|
591
|
-
is_write,
|
|
592
|
-
method_of,
|
|
593
|
-
repo_target
|
|
594
|
-
};
|
|
595
|
-
|
|
596
551
|
// scripts/rules/git-argv.ts
|
|
597
552
|
var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/u;
|
|
598
553
|
var WRAPPER_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -1200,7 +1155,141 @@ var implementation_cut = {
|
|
|
1200
1155
|
uncut_run_issue
|
|
1201
1156
|
};
|
|
1202
1157
|
|
|
1203
|
-
// scripts/rules/
|
|
1158
|
+
// scripts/rules/gh-api.ts
|
|
1159
|
+
var GH_FLAGS = String.raw`(?:-{1,2}[\w-]+(?:[= ][^\s]+)?\s+)*`;
|
|
1160
|
+
var GH_API_COMMAND = new RegExp(String.raw`^gh\s+${GH_FLAGS}api\s`, "u");
|
|
1161
|
+
var API_METHOD = /(?:^|\s)(?:--method|-X)[= ]([A-Za-z]+)/u;
|
|
1162
|
+
var API_FIELD = /(?:^|\s)(?:--raw-field|--field|--input|-F|-f)(?:[= ]|$)/u;
|
|
1163
|
+
var READ_METHOD = "GET";
|
|
1164
|
+
var REPO_PATH = /repos\/([^/\s'"?]+)\/([^/\s'"?]+)/u;
|
|
1165
|
+
var OWNER_GROUP = 1;
|
|
1166
|
+
var REPO_GROUP = 2;
|
|
1167
|
+
var OWNER_PLACEHOLDERS = /* @__PURE__ */ new Set(["{owner}", ":owner"]);
|
|
1168
|
+
function is_gh_api(segment) {
|
|
1169
|
+
return GH_API_COMMAND.test(segment);
|
|
1170
|
+
}
|
|
1171
|
+
function method_of(segment) {
|
|
1172
|
+
return API_METHOD.exec(segment)?.[1]?.toUpperCase();
|
|
1173
|
+
}
|
|
1174
|
+
function is_read(segment) {
|
|
1175
|
+
const method = method_of(segment);
|
|
1176
|
+
if (method !== void 0) return method.toUpperCase() === READ_METHOD;
|
|
1177
|
+
return !API_FIELD.test(segment);
|
|
1178
|
+
}
|
|
1179
|
+
function is_write(segment) {
|
|
1180
|
+
return !is_read(segment);
|
|
1181
|
+
}
|
|
1182
|
+
function repo_target(segment) {
|
|
1183
|
+
const match = REPO_PATH.exec(segment);
|
|
1184
|
+
if (match === null) return void 0;
|
|
1185
|
+
return { owner: match[OWNER_GROUP] ?? "", repo: match[REPO_GROUP] ?? "" };
|
|
1186
|
+
}
|
|
1187
|
+
function is_owner_placeholder(owner) {
|
|
1188
|
+
return OWNER_PLACEHOLDERS.has(owner);
|
|
1189
|
+
}
|
|
1190
|
+
var gh_api = {
|
|
1191
|
+
GH_FLAGS,
|
|
1192
|
+
is_gh_api,
|
|
1193
|
+
is_owner_placeholder,
|
|
1194
|
+
is_read,
|
|
1195
|
+
is_write,
|
|
1196
|
+
method_of,
|
|
1197
|
+
repo_target
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
// scripts/rules/prior-comment-read.ts
|
|
1201
|
+
var ISSUE_PATH_NUMBER = /issues\/(\d+)/gu;
|
|
1202
|
+
var ISSUE_VIEW_NUMBER = /\bissue\s+view\s+(?:-{1,2}\S+\s+)*(\d+)/u;
|
|
1203
|
+
var ISSUE_READ_ARGUMENTS = /\bissue:read\b([\s\d]*)/u;
|
|
1204
|
+
var A_NUMBER = /^\d+$/u;
|
|
1205
|
+
var COMMENTS_ENDPOINT = /issues\/\d+\/comments\b/u;
|
|
1206
|
+
var VIEW_WITH_COMMENTS = /\bissue\s+view\b[^\n]*--comments\b/u;
|
|
1207
|
+
var ISSUE_READ_COMMAND = /\bissue:read\b/u;
|
|
1208
|
+
function add_issue_read_numbers(command, numbers) {
|
|
1209
|
+
const match = ISSUE_READ_ARGUMENTS.exec(command);
|
|
1210
|
+
if (match === null) return;
|
|
1211
|
+
const tokens = (match[1] ?? "").split(/\s+/u);
|
|
1212
|
+
for (const token of tokens) {
|
|
1213
|
+
if (A_NUMBER.test(token)) numbers.add(Number(token));
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
function issue_numbers_of(command) {
|
|
1217
|
+
const numbers = /* @__PURE__ */ new Set();
|
|
1218
|
+
for (const match of command.matchAll(ISSUE_PATH_NUMBER)) numbers.add(Number(match[1]));
|
|
1219
|
+
const view = ISSUE_VIEW_NUMBER.exec(command);
|
|
1220
|
+
if (view !== null) numbers.add(Number(view[1]));
|
|
1221
|
+
add_issue_read_numbers(command, numbers);
|
|
1222
|
+
return numbers;
|
|
1223
|
+
}
|
|
1224
|
+
function fetches_comments(command) {
|
|
1225
|
+
return COMMENTS_ENDPOINT.test(command) || VIEW_WITH_COMMENTS.test(command) || ISSUE_READ_COMMAND.test(command);
|
|
1226
|
+
}
|
|
1227
|
+
function reads_target_comments(command, target) {
|
|
1228
|
+
if (!fetches_comments(command)) return false;
|
|
1229
|
+
for (const number of issue_numbers_of(command)) {
|
|
1230
|
+
if (target.has(number)) return true;
|
|
1231
|
+
}
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
function already_read_comments_for(tail, command) {
|
|
1235
|
+
const target = issue_numbers_of(command);
|
|
1236
|
+
if (target.size === 0) return false;
|
|
1237
|
+
return tail_commands.prior_bash_commands(tail).some((prior) => reads_target_comments(prior, target));
|
|
1238
|
+
}
|
|
1239
|
+
function already_read_for_call(tail, call) {
|
|
1240
|
+
if (call.name !== cost_blocks.BASH_TOOL) return false;
|
|
1241
|
+
return already_read_comments_for(tail, time_shell.bash_command(call.input));
|
|
1242
|
+
}
|
|
1243
|
+
var prior_comment_read = {
|
|
1244
|
+
already_read_comments_for,
|
|
1245
|
+
already_read_for_call,
|
|
1246
|
+
fetches_comments,
|
|
1247
|
+
issue_numbers_of
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1250
|
+
// scripts/rules/issue-comments.ts
|
|
1251
|
+
var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
|
|
1252
|
+
var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
|
|
1253
|
+
var FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u;
|
|
1254
|
+
var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
|
|
1255
|
+
var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
|
|
1256
|
+
var NAMES_THE_BODY = /\bbody\b/u;
|
|
1257
|
+
function projects_away_body(segment) {
|
|
1258
|
+
const projection = FIELD_PROJECTION.exec(segment);
|
|
1259
|
+
return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? "");
|
|
1260
|
+
}
|
|
1261
|
+
function is_body_read_segment(segment) {
|
|
1262
|
+
if (projects_away_body(segment)) return false;
|
|
1263
|
+
if (ISSUE_VIEW_COMMAND.test(segment)) return true;
|
|
1264
|
+
return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
|
|
1265
|
+
}
|
|
1266
|
+
function fetches_issue_comments(segment) {
|
|
1267
|
+
if (!FETCHES_COMMENTS.test(segment)) return false;
|
|
1268
|
+
return ISSUE_VIEW_COMMAND.test(segment) || gh_api.is_gh_api(segment) && ISSUES_PATH.test(segment);
|
|
1269
|
+
}
|
|
1270
|
+
function is_body_only_issue_read(command) {
|
|
1271
|
+
const segments = shell_segments.segments_of(command);
|
|
1272
|
+
if (segments.some((segment) => fetches_issue_comments(segment))) return false;
|
|
1273
|
+
return segments.some((segment) => is_body_read_segment(segment));
|
|
1274
|
+
}
|
|
1275
|
+
function reads_issue_comments(command) {
|
|
1276
|
+
return shell_segments.segments_of(command).some((segment) => fetches_issue_comments(segment));
|
|
1277
|
+
}
|
|
1278
|
+
var ISSUE_COMMENTS_REASON = "\u26D4 an Issue's comments are part of the Issue: read them before implementing, not only the body. A decision recorded after the body was written lives only in a comment \u2014 a corrected diagnosis (joshuafolkken/kit#1537), a changed default and an added acceptance criterion (joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) \u2014 and nothing in the body says it was superseded, so a body-only reader builds the wrong thing and sees no contradiction. Reissue this read with the comments included: `gh api repos/{owner}/{repo}/issues/<N>/comments --jq '.[] | {user: .user.login, created_at, body}'` beside the body read, or `gh issue view <N> --comments` where GraphQL is reachable. Then the later text is the agreement in force \u2014 a comment supersedes the body it contradicts \u2014 except for two answers that are not the run's to make: work a comment reassigns to another Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a reason to exist stops the run with a `confirmation` Telegram. The procedure is `.claude/skills/workflow-commands/issue-comments.md`. Every body-only read is refused until the comments are read.";
|
|
1279
|
+
var ROW5 = {
|
|
1280
|
+
id: "issue-comments",
|
|
1281
|
+
is_trigger: bash_triggers.on_bash_command(is_body_only_issue_read),
|
|
1282
|
+
reason: ISSUE_COMMENTS_REASON,
|
|
1283
|
+
already_satisfied: prior_comment_read.already_read_for_call,
|
|
1284
|
+
keeps: bash_triggers.on_bash_command(reads_issue_comments)
|
|
1285
|
+
};
|
|
1286
|
+
var issue_comments = {
|
|
1287
|
+
ISSUE_COMMENTS_REASON,
|
|
1288
|
+
ROW: ROW5,
|
|
1289
|
+
is_body_only_issue_read
|
|
1290
|
+
};
|
|
1291
|
+
|
|
1292
|
+
// scripts/rules/issue-fold-rule.ts
|
|
1204
1293
|
var FOLD_NAMES = /* @__PURE__ */ new Set(["issue:fold"]);
|
|
1205
1294
|
function runs_the_fold(command) {
|
|
1206
1295
|
return shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, FOLD_NAMES));
|
|
@@ -1210,14 +1299,14 @@ function already_folded(tail, _call) {
|
|
|
1210
1299
|
return tail_commands.prior_bash_commands(tail).some((command) => runs_the_fold(command));
|
|
1211
1300
|
}
|
|
1212
1301
|
var ISSUE_FOLD_REASON = '\u26D4 issue fold: this run has filed an Issue before and is filing another with no `pnpm josh issue:fold` between them. Several findings from one session fold into one Issue by default \u2014 the filing-time counterpart to the split assessment, whose two questions it reuses: are the findings separable, and does the whole clearly exceed one verification gate (the size half is `split:assess`, called not recomputed). Reissue after folding: `pnpm josh issue:fold "<title>" "<title>" \u2026` \u2014 `fold` means file one Issue covering them, `separate` means file them apart (separable findings whose combined size clears the split guide), and the default is `fold`. Add `--not-separable` when they are really one deliverable. The procedure is `.claude/skills/workflow-commands/split-assessment.md` \u2192 "The question". Every later filing is refused until the fold is on the transcript.';
|
|
1213
|
-
var
|
|
1302
|
+
var ROW6 = {
|
|
1214
1303
|
id: "issue-fold",
|
|
1215
1304
|
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
1216
1305
|
reason: ISSUE_FOLD_REASON,
|
|
1217
1306
|
already_satisfied: already_folded,
|
|
1218
1307
|
keeps: bash_triggers.on_bash_command(runs_the_fold)
|
|
1219
1308
|
};
|
|
1220
|
-
var
|
|
1309
|
+
var issue_fold_rule = { ISSUE_FOLD_REASON, ROW: ROW6, already_folded, runs_the_fold };
|
|
1221
1310
|
|
|
1222
1311
|
// scripts/rules/josh-git-bare.ts
|
|
1223
1312
|
var GIT_COMMAND2 = /* @__PURE__ */ new Set(["git"]);
|
|
@@ -1233,7 +1322,7 @@ function is_bare_josh_git(command) {
|
|
|
1233
1322
|
return shell_segments.segments_of(command).some((segment) => is_bare_segment(segment));
|
|
1234
1323
|
}
|
|
1235
1324
|
var JOSH_GIT_BARE_REASON = '\u26D4 bare `pnpm josh git`: this prompts to confirm the staging, and with no TTY the prompt falls to its cancel branch \u2014 `Checking unstaged files... Found` then `Operation cancelled.` \u2014 so the call throws away the time it takes to fail (about 31 seconds in one measured lane) and you reissue it with `-y` anyway (joshuafolkken/kit#2297). Run it unattended from the start: `pnpm josh git -y "<title> #<N>"`. The recovery form that opens the PR for a push that already landed is `pnpm josh git -y --skip-commit --skip-push`. This rule fires on every occurrence, not once per run.';
|
|
1236
|
-
var
|
|
1325
|
+
var ROW7 = {
|
|
1237
1326
|
id: "josh-git-bare",
|
|
1238
1327
|
is_trigger: bash_triggers.on_bash_command(is_bare_josh_git),
|
|
1239
1328
|
reason: JOSH_GIT_BARE_REASON,
|
|
@@ -1241,7 +1330,7 @@ var ROW6 = {
|
|
|
1241
1330
|
};
|
|
1242
1331
|
var josh_git_bare = {
|
|
1243
1332
|
JOSH_GIT_BARE_REASON,
|
|
1244
|
-
ROW:
|
|
1333
|
+
ROW: ROW7,
|
|
1245
1334
|
is_bare_josh_git
|
|
1246
1335
|
};
|
|
1247
1336
|
|
|
@@ -1255,7 +1344,7 @@ function is_carry_conflict(command, is_lane_child = lane_child_marker.is_child_o
|
|
|
1255
1344
|
return is_lane_child;
|
|
1256
1345
|
}
|
|
1257
1346
|
var LANE_CARRY_CONFLICT_REASON = '\u26D4 lane child carry conflict: this session is a dispatched lane child, and `pnpm josh run:merge` / `pnpm josh run:carry` are the parent backlogrun budget commands \u2014 a child must never run them. The live carry record here belongs to the parent that launched this child, not to a competing run: the parent holds the one run budget for the repository, and every lane shares the common git directory it is keyed on, so a live owner a lane child reads is always its parent (joshuafolkken/kit#2267, measured 2026-09-21 in backlogrun #2252 \u2014 a child read run:merge `busy` as a competitor and parked #2258 unimplemented). Do not run this command, and do not treat it as a conflict: no `needs-decision`, no `confirmation` Telegram, no question to a person \u2014 a dispatched child cannot ask, and there is nothing to decide. Continue implementing this Issue; the parent owns the budget and counts the outcome at the return. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` and `backlogrun-progress.md` \u2192 "The hand-off". This rule fires on every occurrence, not once per run.';
|
|
1258
|
-
var
|
|
1347
|
+
var ROW8 = {
|
|
1259
1348
|
id: "lane-carry-conflict",
|
|
1260
1349
|
is_trigger: bash_triggers.on_bash_command(is_carry_conflict),
|
|
1261
1350
|
reason: LANE_CARRY_CONFLICT_REASON,
|
|
@@ -1263,7 +1352,7 @@ var ROW7 = {
|
|
|
1263
1352
|
};
|
|
1264
1353
|
var lane_carry_conflict = {
|
|
1265
1354
|
LANE_CARRY_CONFLICT_REASON,
|
|
1266
|
-
ROW:
|
|
1355
|
+
ROW: ROW8,
|
|
1267
1356
|
invokes_budget_command,
|
|
1268
1357
|
is_carry_conflict
|
|
1269
1358
|
};
|
|
@@ -1316,13 +1405,73 @@ function is_interactive_ask(call, is_lane_child = lane_child_marker.is_child_of(
|
|
|
1316
1405
|
return is_lane_child;
|
|
1317
1406
|
}
|
|
1318
1407
|
var LANE_INTERACTIVE_ASK_REASON = '\u26D4 lane child interactive ask: this session is a dispatched lane child and cannot ask a person, so this call is refused. Left to run it would end the turn with nothing on the Issue \u2014 the parent then finds an OPEN Issue with `in-progress` and no question (joshuafolkken/kit#2201; joshuafolkken/kit#2034 placed the same rule one tool-call too late). Do not ask: park the decision instead. Apply `needs-decision` and post a comment carrying the question, the options and whether work was stashed \u2014 `gh api repos/{owner}/{repo}/issues/<N>/labels -f \'labels[]=needs-decision\'` then `pnpm josh issue:comment <N> --body-file <path>` \u2014 then send the `confirmation` Telegram and stop. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` \u2192 "A lane child records its park before it stops", and the park itself is `backlogrun-park.md` \u2192 "park and continue". This rule fires on every occurrence, not once per run.';
|
|
1319
|
-
var
|
|
1408
|
+
var ROW9 = {
|
|
1320
1409
|
id: "lane-interactive-ask",
|
|
1321
1410
|
is_trigger: is_interactive_ask,
|
|
1322
1411
|
reason: LANE_INTERACTIVE_ASK_REASON,
|
|
1323
1412
|
decide: () => true
|
|
1324
1413
|
};
|
|
1325
|
-
var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW:
|
|
1414
|
+
var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW9, is_interactive_ask };
|
|
1415
|
+
|
|
1416
|
+
// scripts/rules/lane-split-park.ts
|
|
1417
|
+
var EPIC_COMMANDS = /* @__PURE__ */ new Set(["epic"]);
|
|
1418
|
+
var PROMOTED_ISSUE = /--promote[=\s]+["']?(?<issue>\d+)\b/u;
|
|
1419
|
+
var LABEL_TARGETS = [
|
|
1420
|
+
/\/issues\/(?<issue>\d+)\/labels\b/u,
|
|
1421
|
+
/\bissue\s+edit\s+["']?(?<issue>\d+)\b/u
|
|
1422
|
+
];
|
|
1423
|
+
var ISSUE_URL = /--issue-url[=\s]+["']?([^\s"']+)/u;
|
|
1424
|
+
var URL_ISSUE = /\/issues\/(\d+)\b/u;
|
|
1425
|
+
function promoted_in(segment) {
|
|
1426
|
+
if (!shell_segments.is_josh_command(segment, EPIC_COMMANDS)) return void 0;
|
|
1427
|
+
return PROMOTED_ISSUE.exec(segment)?.groups?.["issue"];
|
|
1428
|
+
}
|
|
1429
|
+
function promoted_issues(commands) {
|
|
1430
|
+
const segments = commands.flatMap((command) => shell_segments.segments_of(command));
|
|
1431
|
+
const issues = segments.map((segment) => promoted_in(segment));
|
|
1432
|
+
return new Set(issues.filter((issue) => issue !== void 0));
|
|
1433
|
+
}
|
|
1434
|
+
function label_target(command) {
|
|
1435
|
+
for (const pattern of LABEL_TARGETS) {
|
|
1436
|
+
const issue = pattern.exec(command)?.groups?.["issue"];
|
|
1437
|
+
if (issue !== void 0) return issue;
|
|
1438
|
+
}
|
|
1439
|
+
return void 0;
|
|
1440
|
+
}
|
|
1441
|
+
function is_park_of(command, issue) {
|
|
1442
|
+
return lane_park.records_the_park(command) && label_target(command) === issue;
|
|
1443
|
+
}
|
|
1444
|
+
function notified_issue(command) {
|
|
1445
|
+
const [, url = ""] = ISSUE_URL.exec(command) ?? [];
|
|
1446
|
+
const [, issue] = URL_ISSUE.exec(url) ?? [];
|
|
1447
|
+
return issue;
|
|
1448
|
+
}
|
|
1449
|
+
function is_notify_of(command, issue) {
|
|
1450
|
+
return lane_park.is_confirmation_notify(command) && notified_issue(command) === issue;
|
|
1451
|
+
}
|
|
1452
|
+
function is_candidate(command) {
|
|
1453
|
+
const issue = lane_child_marker.marked_issue();
|
|
1454
|
+
if (issue === void 0) return false;
|
|
1455
|
+
if (!is_park_of(command, issue) && !is_notify_of(command, issue)) return false;
|
|
1456
|
+
return lane_child_marker.is_child_of(process.cwd());
|
|
1457
|
+
}
|
|
1458
|
+
function decide3(_call, run2) {
|
|
1459
|
+
const issue = lane_child_marker.marked_issue();
|
|
1460
|
+
const commands = tail_commands.prior_bash_commands(time_density_hook.read_tail(run2.transcript));
|
|
1461
|
+
return issue !== void 0 && promoted_issues(commands).has(issue);
|
|
1462
|
+
}
|
|
1463
|
+
var LANE_SPLIT_PARK_REASON = '\u26D4 lane split park: this session is a dispatched lane child that promoted its own Issue to an epic this run, and a split is not a park \u2014 do not apply `needs-decision` to the promoted epic and do not send a `confirmation` Telegram. The parent reads an open Issue carrying `epic` as a split and continues through its children; a question here has nobody to answer it (joshuafolkken/kit#3296, measured 2026-10-06 when the child for #3255 parked its promoted epic). If what remains of the original work needs a person, park that child issue instead, then end the turn. The procedure is `.claude/skills/workflow-commands/backlogrun-park.md` \u2192 "Splitting a child mid-run". This rule fires on every occurrence, not once per run.';
|
|
1464
|
+
var ROW10 = {
|
|
1465
|
+
id: "lane-split-park",
|
|
1466
|
+
is_trigger: bash_triggers.on_bash_command(is_candidate),
|
|
1467
|
+
reason: LANE_SPLIT_PARK_REASON,
|
|
1468
|
+
decide: decide3
|
|
1469
|
+
};
|
|
1470
|
+
var lane_split_park = {
|
|
1471
|
+
LANE_SPLIT_PARK_REASON,
|
|
1472
|
+
ROW: ROW10,
|
|
1473
|
+
promoted_issues
|
|
1474
|
+
};
|
|
1326
1475
|
|
|
1327
1476
|
// scripts/rules/lane-switch-main.ts
|
|
1328
1477
|
var SWITCH_SUBCOMMAND = "switch";
|
|
@@ -1353,7 +1502,7 @@ function is_switch_away_from_lane(command, is_lane_child = lane_child_marker.is_
|
|
|
1353
1502
|
return switch_targets(command).some((target) => target !== own_branch);
|
|
1354
1503
|
}
|
|
1355
1504
|
var LANE_SWITCH_MAIN_REASON = "\u26D4 lane child on `git switch main`: this session is a dispatched lane child, and switching to the default branch here always fails \u2014 a lane is a linked work tree, the primary checkout holds the default branch, and git allows one branch in one work tree at a time (`fatal: main is already used by worktree at <the primary checkout>`, measured 11 times in the 2026-09-21 backlogrun). Do not switch: the lane is already on its own issue branch, branched from a fresh default before it opened, and the latest default is brought in during the gate by `pnpm josh main:merge`. So skip this step and keep implementing on the current branch \u2014 no `git switch`, no `git pull`. `pnpm josh main:sync` (`josh ms`) is refused in a lane for the same reason, and a lane is finished with `pnpm josh lane:close <issue-number>`. The parent runs `git switch main && git pull` in the primary checkout before each lane opens, never the child \u2014 `backlogrun-lanes.md` and `backlogrun-child.md` are the single sources. This rule fires on every occurrence, not once per run.";
|
|
1356
|
-
var
|
|
1505
|
+
var ROW11 = {
|
|
1357
1506
|
id: "lane-switch-main",
|
|
1358
1507
|
is_trigger: bash_triggers.on_bash_command(is_switch_away_from_lane),
|
|
1359
1508
|
reason: LANE_SWITCH_MAIN_REASON,
|
|
@@ -1361,12 +1510,138 @@ var ROW9 = {
|
|
|
1361
1510
|
};
|
|
1362
1511
|
var lane_switch_main = {
|
|
1363
1512
|
LANE_SWITCH_MAIN_REASON,
|
|
1364
|
-
ROW:
|
|
1513
|
+
ROW: ROW11,
|
|
1365
1514
|
is_switch_away_from_lane,
|
|
1366
1515
|
switch_target,
|
|
1367
1516
|
switch_targets
|
|
1368
1517
|
};
|
|
1369
1518
|
|
|
1519
|
+
// scripts/rules/decision-oracle-tokens.ts
|
|
1520
|
+
var oracle_tokens = {
|
|
1521
|
+
REQUIRED: "required",
|
|
1522
|
+
SKIP: "skip",
|
|
1523
|
+
STOP: "stop",
|
|
1524
|
+
WAIT: "wait",
|
|
1525
|
+
RETRY: "retry",
|
|
1526
|
+
OVER: "over",
|
|
1527
|
+
UNKNOWN: "unknown",
|
|
1528
|
+
NONE: "none",
|
|
1529
|
+
NOT_A_LANE: "not-a-lane",
|
|
1530
|
+
BUSY: "busy",
|
|
1531
|
+
HUMAN_REVIEW: "human-review",
|
|
1532
|
+
ISSUE_N_ARG: "<N>",
|
|
1533
|
+
EXCLUDE_ARG: "[--exclude <n>...]"
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
// scripts/rules/decision-oracle-batch.ts
|
|
1537
|
+
var {
|
|
1538
|
+
BUSY,
|
|
1539
|
+
EXCLUDE_ARG,
|
|
1540
|
+
HUMAN_REVIEW,
|
|
1541
|
+
ISSUE_N_ARG,
|
|
1542
|
+
NONE,
|
|
1543
|
+
NOT_A_LANE,
|
|
1544
|
+
OVER,
|
|
1545
|
+
RETRY,
|
|
1546
|
+
STOP,
|
|
1547
|
+
UNKNOWN,
|
|
1548
|
+
WAIT
|
|
1549
|
+
} = oracle_tokens;
|
|
1550
|
+
var PRE_GATE_CUT_MD = ".claude/skills/workflow-commands/pre-gate-cut.md";
|
|
1551
|
+
var BACKLOGRUN_MD = ".claude/skills/workflow-commands/backlogrun.md";
|
|
1552
|
+
var RUN_MERGE_REFERENCE = "docs/josh-commands-run.md \u2192 `josh run:merge`";
|
|
1553
|
+
var EPIC_NEXT_REFERENCE = "docs/josh-commands-backlog.md \u2192 `josh epic:next`";
|
|
1554
|
+
var BACKLOG_BUDGET_REFERENCE = "docs/josh-commands-backlog.md \u2192 `josh backlog:budget`";
|
|
1555
|
+
var RUN_LIVENESS_REFERENCE = "docs/josh-commands-run.md \u2192 `josh run:liveness`";
|
|
1556
|
+
var AUTO_OK_NEXT_REFERENCE = "docs/josh-commands-backlog.md \u2192 `josh auto-ok:next`";
|
|
1557
|
+
var RUN_CUT_CMD = "run:cut";
|
|
1558
|
+
var BATCH_ORACLES = [
|
|
1559
|
+
{
|
|
1560
|
+
name: "run:cut:resume",
|
|
1561
|
+
command: RUN_CUT_CMD,
|
|
1562
|
+
decision: "Whether a lane child is resuming prior work or starting fresh",
|
|
1563
|
+
args: `--resume ${ISSUE_N_ARG}`,
|
|
1564
|
+
vocabulary: ["fresh", "resume", NOT_A_LANE],
|
|
1565
|
+
single_source: PRE_GATE_CUT_MD
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
name: "run:cut:gate",
|
|
1569
|
+
command: RUN_CUT_CMD,
|
|
1570
|
+
decision: "Whether a lane child should take its pre-gate cut now",
|
|
1571
|
+
args: ISSUE_N_ARG,
|
|
1572
|
+
vocabulary: ["cut", NOT_A_LANE],
|
|
1573
|
+
single_source: PRE_GATE_CUT_MD
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
name: "backlog:budget",
|
|
1577
|
+
decision: "Whether a backlogrun may start more work, keep watching, or finish",
|
|
1578
|
+
args: "",
|
|
1579
|
+
vocabulary: ["run", "watch", STOP],
|
|
1580
|
+
single_source: BACKLOG_BUDGET_REFERENCE
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
name: "run:liveness",
|
|
1584
|
+
decision: "Whether a delegated unit is still working or has stopped without reporting",
|
|
1585
|
+
args: `${ISSUE_N_ARG} --output <path>`,
|
|
1586
|
+
vocabulary: ["alive", "stopped", "settled", "undetermined"],
|
|
1587
|
+
single_source: RUN_LIVENESS_REFERENCE
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
name: "stash:pop",
|
|
1591
|
+
decision: "Whether a named stash entry exists and can be applied",
|
|
1592
|
+
args: "<message>",
|
|
1593
|
+
vocabulary: ["popped", "conflicted", "no-match", "ambiguous"],
|
|
1594
|
+
single_source: ".claude/skills/workflow-commands/prerequisite.md"
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
name: "backlog:next",
|
|
1598
|
+
decision: "Which issue the backlog offers next, or whether to wait or stop",
|
|
1599
|
+
args: EXCLUDE_ARG,
|
|
1600
|
+
vocabulary: [WAIT, STOP, RETRY, "error", NONE],
|
|
1601
|
+
single_source: BACKLOGRUN_MD
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
name: "auto-ok:next",
|
|
1605
|
+
decision: "The next opted-in issue outside any epic, or none",
|
|
1606
|
+
args: EXCLUDE_ARG,
|
|
1607
|
+
vocabulary: [NONE],
|
|
1608
|
+
single_source: AUTO_OK_NEXT_REFERENCE
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
name: "epic:next",
|
|
1612
|
+
decision: "The next runnable child of an epic, or the epic's status",
|
|
1613
|
+
args: "<epic>",
|
|
1614
|
+
vocabulary: [WAIT, STOP, "complete"],
|
|
1615
|
+
single_source: EPIC_NEXT_REFERENCE
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
name: "run:merge",
|
|
1619
|
+
decision: "The post-merge batch step: confirm child, advance, or report a stop condition",
|
|
1620
|
+
args: ISSUE_N_ARG,
|
|
1621
|
+
vocabulary: [OVER, HUMAN_REVIEW, STOP, RETRY, BUSY],
|
|
1622
|
+
single_source: RUN_MERGE_REFERENCE
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
name: "run:step",
|
|
1626
|
+
decision: "The run\u2019s next single action, computed from the event stream, carry and issue state",
|
|
1627
|
+
args: ISSUE_N_ARG,
|
|
1628
|
+
// The non-command answers: `run:step` prints a runnable command for a phase with one to run, and
|
|
1629
|
+
// one of these verdicts otherwise. A command line is not a vocabulary token, exactly as an issue
|
|
1630
|
+
// number is not one for `backlog:next`.
|
|
1631
|
+
vocabulary: [
|
|
1632
|
+
"implement",
|
|
1633
|
+
HUMAN_REVIEW,
|
|
1634
|
+
"update-deps",
|
|
1635
|
+
"already-done",
|
|
1636
|
+
"keep-work",
|
|
1637
|
+
WAIT,
|
|
1638
|
+
STOP,
|
|
1639
|
+
UNKNOWN
|
|
1640
|
+
],
|
|
1641
|
+
single_source: BACKLOGRUN_MD
|
|
1642
|
+
}
|
|
1643
|
+
];
|
|
1644
|
+
|
|
1370
1645
|
// scripts/run/run-stage.ts
|
|
1371
1646
|
var KICKOFF = "kickoff";
|
|
1372
1647
|
var HALFRUN = "halfrun";
|
|
@@ -1409,7 +1684,7 @@ function state_of(facts) {
|
|
|
1409
1684
|
if (facts.is_halfrun_stopped) return HALFRUN_STOPPED;
|
|
1410
1685
|
return facts.is_planned ? PLANNED : FRESH;
|
|
1411
1686
|
}
|
|
1412
|
-
function
|
|
1687
|
+
function decide4(state, command) {
|
|
1413
1688
|
const is_reached = STATES.indexOf(state) > COMMANDS.indexOf(command);
|
|
1414
1689
|
const start = is_reached ? REACHED : STARTS[state];
|
|
1415
1690
|
return { state, command, start, end: ENDS[command], is_reached };
|
|
@@ -1426,7 +1701,7 @@ function format_decision(issue, decision) {
|
|
|
1426
1701
|
return `stage #${issue} \u2014 ${fields.join(" \xB7 ")}`;
|
|
1427
1702
|
}
|
|
1428
1703
|
function cell(state, command) {
|
|
1429
|
-
const decision =
|
|
1704
|
+
const decision = decide4(state, command);
|
|
1430
1705
|
return decision.is_reached ? REACHED_CELL : `${decision.start}${CELL_ARROW}${decision.end}`;
|
|
1431
1706
|
}
|
|
1432
1707
|
function table_rows() {
|
|
@@ -1451,7 +1726,7 @@ var run_stage = {
|
|
|
1451
1726
|
REACHED,
|
|
1452
1727
|
START_TOKENS,
|
|
1453
1728
|
STATES,
|
|
1454
|
-
decide:
|
|
1729
|
+
decide: decide4,
|
|
1455
1730
|
format_decision,
|
|
1456
1731
|
next_commands,
|
|
1457
1732
|
next_line,
|
|
@@ -1470,35 +1745,15 @@ var RUN_ENTRY_ORACLE = {
|
|
|
1470
1745
|
};
|
|
1471
1746
|
|
|
1472
1747
|
// scripts/rules/decision-oracle.ts
|
|
1748
|
+
var { BUSY: BUSY2, ISSUE_N_ARG: ISSUE_N_ARG2, NONE: NONE2, OVER: OVER2, REQUIRED, SKIP, UNKNOWN: UNKNOWN2 } = oracle_tokens;
|
|
1473
1749
|
var CHAIN_RULE_MD = ".claude/skills/workflow-commands/chain-rule.md";
|
|
1474
1750
|
var BACKLOGRUN_PROGRESS_MD = ".claude/skills/workflow-commands/backlogrun-progress.md";
|
|
1475
1751
|
var SKILL_2E = ".claude/skills/workflow-commands/issue-scout.md";
|
|
1476
1752
|
var SPLIT_ASSESSMENT_QUESTION = ".claude/skills/workflow-commands/split-assessment.md \u2192 The question";
|
|
1477
|
-
var PRE_GATE_CUT_MD = ".claude/skills/workflow-commands/pre-gate-cut.md";
|
|
1478
|
-
var BACKLOGRUN_MD = ".claude/skills/workflow-commands/backlogrun.md";
|
|
1479
|
-
var RUN_MERGE_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh run:merge`";
|
|
1480
|
-
var EPIC_NEXT_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh epic:next`";
|
|
1481
|
-
var BACKLOG_BUDGET_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh backlog:budget`";
|
|
1482
|
-
var RUN_LIVENESS_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh run:liveness`";
|
|
1483
|
-
var AUTO_OK_NEXT_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh auto-ok:next`";
|
|
1484
|
-
var RUN_CUT_CMD = "run:cut";
|
|
1485
|
-
var REQUIRED = "required";
|
|
1486
|
-
var SKIP = "skip";
|
|
1487
|
-
var STOP = "stop";
|
|
1488
|
-
var WAIT = "wait";
|
|
1489
|
-
var RETRY = "retry";
|
|
1490
|
-
var OVER = "over";
|
|
1491
|
-
var UNKNOWN = "unknown";
|
|
1492
|
-
var NONE = "none";
|
|
1493
|
-
var NOT_A_LANE = "not-a-lane";
|
|
1494
|
-
var BUSY = "busy";
|
|
1495
|
-
var HUMAN_REVIEW = "human-review";
|
|
1496
1753
|
var LIVE = "live";
|
|
1497
1754
|
var STOPPED = "stopped";
|
|
1498
1755
|
var RECONCILED = "reconciled";
|
|
1499
1756
|
var NOTHING_TO_RECONCILE = "nothing to reconcile";
|
|
1500
|
-
var ISSUE_N_ARG = "<N>";
|
|
1501
|
-
var EXCLUDE_ARG = "[--exclude <n>...]";
|
|
1502
1757
|
var DECISION_ORACLES = [
|
|
1503
1758
|
{
|
|
1504
1759
|
name: "delegate",
|
|
@@ -1546,8 +1801,8 @@ var DECISION_ORACLES = [
|
|
|
1546
1801
|
{
|
|
1547
1802
|
name: "run:hold",
|
|
1548
1803
|
decision: "Whether this working tree is free for a run to claim",
|
|
1549
|
-
args: `[${
|
|
1550
|
-
vocabulary: ["hold",
|
|
1804
|
+
args: `[${ISSUE_N_ARG2}]`,
|
|
1805
|
+
vocabulary: ["hold", BUSY2, UNKNOWN2],
|
|
1551
1806
|
single_source: ".claude/skills/workflow-commands/working-tree-hold.md"
|
|
1552
1807
|
},
|
|
1553
1808
|
{
|
|
@@ -1555,20 +1810,20 @@ var DECISION_ORACLES = [
|
|
|
1555
1810
|
command: "cost",
|
|
1556
1811
|
decision: "Whether the session cost exceeds the hand-off threshold",
|
|
1557
1812
|
args: "--cut",
|
|
1558
|
-
vocabulary: [
|
|
1813
|
+
vocabulary: [OVER2, "under"],
|
|
1559
1814
|
single_source: BACKLOGRUN_PROGRESS_MD
|
|
1560
1815
|
},
|
|
1561
1816
|
{
|
|
1562
1817
|
name: "release:scope",
|
|
1563
1818
|
decision: "Whether a release is owed after this merge",
|
|
1564
1819
|
args: "",
|
|
1565
|
-
vocabulary: [REQUIRED, SKIP,
|
|
1820
|
+
vocabulary: [REQUIRED, SKIP, UNKNOWN2],
|
|
1566
1821
|
single_source: ".claude/skills/workflow-commands/followup.md"
|
|
1567
1822
|
},
|
|
1568
1823
|
{
|
|
1569
1824
|
name: "epic:bundle",
|
|
1570
1825
|
decision: "Which epic a newly filed issue belongs to",
|
|
1571
|
-
args:
|
|
1826
|
+
args: ISSUE_N_ARG2,
|
|
1572
1827
|
vocabulary: ["add_to_epic", "create_epic", "ask", "nothing_to_bundle"],
|
|
1573
1828
|
single_source: SKILL_2E
|
|
1574
1829
|
},
|
|
@@ -1604,100 +1859,17 @@ var DECISION_ORACLES = [
|
|
|
1604
1859
|
name: "cases",
|
|
1605
1860
|
decision: "Each boundary a change crosses (network, process, fs, time) and the abnormal cases it owes",
|
|
1606
1861
|
args: "<path...>",
|
|
1607
|
-
vocabulary: ["network", "process", "fs", "time",
|
|
1862
|
+
vocabulary: ["network", "process", "fs", "time", NONE2],
|
|
1608
1863
|
single_source: "prompts/collaboration-workflow/report-format.md \u2192 \u5909\u66F4\u3068\u30C6\u30B9\u30C8"
|
|
1609
1864
|
},
|
|
1610
1865
|
{
|
|
1611
1866
|
name: "issue:state",
|
|
1612
1867
|
decision: "An issue's state, labels, and human-review flag",
|
|
1613
|
-
args:
|
|
1868
|
+
args: ISSUE_N_ARG2,
|
|
1614
1869
|
vocabulary: ["human_review: yes", "human_review: no"],
|
|
1615
1870
|
single_source: ".claude/skills/workflow-commands/needs-human-review.md"
|
|
1616
1871
|
},
|
|
1617
|
-
|
|
1618
|
-
name: "run:cut:resume",
|
|
1619
|
-
command: RUN_CUT_CMD,
|
|
1620
|
-
decision: "Whether a lane child is resuming prior work or starting fresh",
|
|
1621
|
-
args: `--resume ${ISSUE_N_ARG}`,
|
|
1622
|
-
vocabulary: ["fresh", "resume", NOT_A_LANE],
|
|
1623
|
-
single_source: PRE_GATE_CUT_MD
|
|
1624
|
-
},
|
|
1625
|
-
{
|
|
1626
|
-
name: "run:cut:gate",
|
|
1627
|
-
command: RUN_CUT_CMD,
|
|
1628
|
-
decision: "Whether a lane child should take its pre-gate cut now",
|
|
1629
|
-
args: ISSUE_N_ARG,
|
|
1630
|
-
vocabulary: ["cut", NOT_A_LANE],
|
|
1631
|
-
single_source: PRE_GATE_CUT_MD
|
|
1632
|
-
},
|
|
1633
|
-
{
|
|
1634
|
-
name: "backlog:budget",
|
|
1635
|
-
decision: "Whether a backlogrun may start more work, keep watching, or finish",
|
|
1636
|
-
args: "",
|
|
1637
|
-
vocabulary: ["run", "watch", STOP],
|
|
1638
|
-
single_source: BACKLOG_BUDGET_REFERENCE
|
|
1639
|
-
},
|
|
1640
|
-
{
|
|
1641
|
-
name: "run:liveness",
|
|
1642
|
-
decision: "Whether a delegated unit is still working or has stopped without reporting",
|
|
1643
|
-
args: `${ISSUE_N_ARG} --output <path>`,
|
|
1644
|
-
vocabulary: ["alive", "stopped", "settled", "undetermined"],
|
|
1645
|
-
single_source: RUN_LIVENESS_REFERENCE
|
|
1646
|
-
},
|
|
1647
|
-
{
|
|
1648
|
-
name: "stash:pop",
|
|
1649
|
-
decision: "Whether a named stash entry exists and can be applied",
|
|
1650
|
-
args: "<message>",
|
|
1651
|
-
vocabulary: ["popped", "conflicted", "no-match", "ambiguous"],
|
|
1652
|
-
single_source: ".claude/skills/workflow-commands/prerequisite.md"
|
|
1653
|
-
},
|
|
1654
|
-
{
|
|
1655
|
-
name: "backlog:next",
|
|
1656
|
-
decision: "Which issue the backlog offers next, or whether to wait or stop",
|
|
1657
|
-
args: EXCLUDE_ARG,
|
|
1658
|
-
vocabulary: [WAIT, STOP, RETRY, "error", NONE],
|
|
1659
|
-
single_source: BACKLOGRUN_MD
|
|
1660
|
-
},
|
|
1661
|
-
{
|
|
1662
|
-
name: "auto-ok:next",
|
|
1663
|
-
decision: "The next opted-in issue outside any epic, or none",
|
|
1664
|
-
args: EXCLUDE_ARG,
|
|
1665
|
-
vocabulary: [NONE],
|
|
1666
|
-
single_source: AUTO_OK_NEXT_REFERENCE
|
|
1667
|
-
},
|
|
1668
|
-
{
|
|
1669
|
-
name: "epic:next",
|
|
1670
|
-
decision: "The next runnable child of an epic, or the epic's status",
|
|
1671
|
-
args: "<epic>",
|
|
1672
|
-
vocabulary: [WAIT, STOP, "complete"],
|
|
1673
|
-
single_source: EPIC_NEXT_REFERENCE
|
|
1674
|
-
},
|
|
1675
|
-
{
|
|
1676
|
-
name: "run:merge",
|
|
1677
|
-
decision: "The post-merge batch step: confirm child, advance, or report a stop condition",
|
|
1678
|
-
args: ISSUE_N_ARG,
|
|
1679
|
-
vocabulary: [OVER, HUMAN_REVIEW, STOP, RETRY, BUSY],
|
|
1680
|
-
single_source: RUN_MERGE_REFERENCE
|
|
1681
|
-
},
|
|
1682
|
-
{
|
|
1683
|
-
name: "run:step",
|
|
1684
|
-
decision: "The run\u2019s next single action, computed from the event stream, carry and issue state",
|
|
1685
|
-
args: ISSUE_N_ARG,
|
|
1686
|
-
// The non-command answers: `run:step` prints a runnable command for a phase with one to run, and
|
|
1687
|
-
// one of these verdicts otherwise. A command line is not a vocabulary token, exactly as an issue
|
|
1688
|
-
// number is not one for `backlog:next`.
|
|
1689
|
-
vocabulary: [
|
|
1690
|
-
"implement",
|
|
1691
|
-
HUMAN_REVIEW,
|
|
1692
|
-
"update-deps",
|
|
1693
|
-
"already-done",
|
|
1694
|
-
"keep-work",
|
|
1695
|
-
WAIT,
|
|
1696
|
-
STOP,
|
|
1697
|
-
UNKNOWN
|
|
1698
|
-
],
|
|
1699
|
-
single_source: BACKLOGRUN_MD
|
|
1700
|
-
},
|
|
1872
|
+
...BATCH_ORACLES,
|
|
1701
1873
|
{
|
|
1702
1874
|
name: "refactor:scan",
|
|
1703
1875
|
decision: "Whether the refactoring scope still holds high- or medium-priority candidates",
|
|
@@ -1724,7 +1896,7 @@ var DECISION_ORACLES = [
|
|
|
1724
1896
|
decision: "Whether cross-file or cross-repository code duplication exists",
|
|
1725
1897
|
args: "",
|
|
1726
1898
|
vocabulary: ["clean", "clones:"],
|
|
1727
|
-
single_source: "prompts/collaboration-workflow/principles.md \u2192
|
|
1899
|
+
single_source: "prompts/collaboration-workflow/principles.md \u2192 no-clones"
|
|
1728
1900
|
},
|
|
1729
1901
|
{
|
|
1730
1902
|
name: "epic:reconcile",
|
|
@@ -1732,14 +1904,14 @@ var DECISION_ORACLES = [
|
|
|
1732
1904
|
decision: "Whether an epic's declaration matches its recorded relations, and the repair when it does not",
|
|
1733
1905
|
args: "--reconcile <E>",
|
|
1734
1906
|
vocabulary: [RECONCILED, NOTHING_TO_RECONCILE],
|
|
1735
|
-
single_source: "docs/josh-commands-
|
|
1907
|
+
single_source: "docs/josh-commands-backlog.md \u2192 `josh epic --reconcile`"
|
|
1736
1908
|
},
|
|
1737
1909
|
{
|
|
1738
1910
|
name: "lane:list",
|
|
1739
1911
|
decision: "Whether each in-progress issue holds a live lane, and the two-directional difference",
|
|
1740
1912
|
args: "",
|
|
1741
|
-
vocabulary: [LIVE, STOPPED,
|
|
1742
|
-
single_source: "docs/josh-commands-
|
|
1913
|
+
vocabulary: [LIVE, STOPPED, UNKNOWN2],
|
|
1914
|
+
single_source: "docs/josh-commands-run.md \u2192 The `in-progress` / lane difference"
|
|
1743
1915
|
},
|
|
1744
1916
|
RUN_ENTRY_ORACLE
|
|
1745
1917
|
];
|
|
@@ -1821,7 +1993,7 @@ function declaration_for(name) {
|
|
|
1821
1993
|
function firing_oracles() {
|
|
1822
1994
|
return decision_oracle.DECISION_ORACLES.flatMap((oracle) => {
|
|
1823
1995
|
const firing_point = FIRING.get(oracle.name);
|
|
1824
|
-
return firing_point === void 0 ? [] :
|
|
1996
|
+
return firing_point === void 0 ? [] : { oracle, firing_point };
|
|
1825
1997
|
});
|
|
1826
1998
|
}
|
|
1827
1999
|
var oracle_firing = {
|
|
@@ -1911,13 +2083,13 @@ function is_destructive_command(command) {
|
|
|
1911
2083
|
return shell_segments.segments_of(command).some((segment) => is_destructive_segment2(segment));
|
|
1912
2084
|
}
|
|
1913
2085
|
var DESTRUCTIVE_COMMAND_REASON = "\u26D4 destructive command: a recursive forced `rm` (`rm -rf`, `rm -fr`, `rm -r -f`, `rm --recursive --force`), `gh repo delete` / `archive`, `gh repo edit --visibility`, `gh pr close`, or a `gh api -X DELETE repos/\u2026` call deletes or closes something that cannot be restored, which is Tier C and needs explicit current-turn user instruction (`prompts/collaboration-workflow/operating-rules.md`). The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user to run the command in their own terminal. `gh issue close` and removing a label from an Issue (`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every occurrence, not once per run.**";
|
|
1914
|
-
var
|
|
2086
|
+
var ROW12 = {
|
|
1915
2087
|
id: "destructive-command",
|
|
1916
2088
|
is_trigger: bash_triggers.on_bash_command(is_destructive_command),
|
|
1917
2089
|
reason: DESTRUCTIVE_COMMAND_REASON,
|
|
1918
2090
|
decide: () => true
|
|
1919
2091
|
};
|
|
1920
|
-
var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW:
|
|
2092
|
+
var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW12, is_destructive_command };
|
|
1921
2093
|
|
|
1922
2094
|
// scripts/rules/direct-pr-create.ts
|
|
1923
2095
|
var GH_COMMAND2 = "gh";
|
|
@@ -1972,13 +2144,13 @@ function is_direct_pr_create(command) {
|
|
|
1972
2144
|
return shell_segments.segments_of(command).some((segment) => is_pr_create_segment(segment));
|
|
1973
2145
|
}
|
|
1974
2146
|
var DIRECT_PR_CREATE_REASON = "\u26D4 direct PR creation: `gh pr create` (or a `gh api` POST to `repos/<o>/<r>/pulls`) bypasses `build_body`, which writes the `closes #N` line \u2014 the Issue would stay open after the merge (`CLAUDE.md` \u2192 Git Rules). Open the PR with `pnpm josh pr` instead; after a failed push, fix, push, then run `pnpm josh pr`. **This rule fires on every occurrence, not once per run.**";
|
|
1975
|
-
var
|
|
2147
|
+
var ROW13 = {
|
|
1976
2148
|
id: "direct-pr-create",
|
|
1977
2149
|
is_trigger: bash_triggers.on_bash_command(is_direct_pr_create),
|
|
1978
2150
|
reason: DIRECT_PR_CREATE_REASON,
|
|
1979
2151
|
decide: () => true
|
|
1980
2152
|
};
|
|
1981
|
-
var direct_pr_create = { DIRECT_PR_CREATE_REASON, ROW:
|
|
2153
|
+
var direct_pr_create = { DIRECT_PR_CREATE_REASON, ROW: ROW13, is_direct_pr_create };
|
|
1982
2154
|
|
|
1983
2155
|
// scripts/rules/worktree-guard.ts
|
|
1984
2156
|
var CHECKOUT_SUBCOMMAND = "checkout";
|
|
@@ -2040,14 +2212,14 @@ function is_unauthorized_worktree_change(command) {
|
|
|
2040
2212
|
return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
|
|
2041
2213
|
}
|
|
2042
2214
|
var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>`, a forced `git clean` and an unauthorized `git stash` discard work or mutate the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
|
|
2043
|
-
var
|
|
2215
|
+
var ROW14 = {
|
|
2044
2216
|
id: "worktree-mutation",
|
|
2045
2217
|
is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
|
|
2046
2218
|
reason: WORKTREE_MUTATION_REASON,
|
|
2047
2219
|
decide: () => true
|
|
2048
2220
|
};
|
|
2049
2221
|
var worktree_guard = {
|
|
2050
|
-
ROW:
|
|
2222
|
+
ROW: ROW14,
|
|
2051
2223
|
WORKTREE_MUTATION_REASON,
|
|
2052
2224
|
is_unauthorized_worktree_change,
|
|
2053
2225
|
touches_worktree
|
|
@@ -2075,14 +2247,14 @@ function is_index_segment(segment) {
|
|
|
2075
2247
|
function is_index_mutation(command) {
|
|
2076
2248
|
return shell_segments.segments_of(command).some((segment) => is_index_segment(segment));
|
|
2077
2249
|
}
|
|
2078
|
-
var INDEX_MUTATION_REASON = '\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user\'s staged snapshot, which `CLAUDE.md` \u2192 Git Rules forbids on your own judgement (`prompts/collaboration-workflow/operating-rules.md` \u2192 "
|
|
2079
|
-
var
|
|
2250
|
+
var INDEX_MUTATION_REASON = '\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user\'s staged snapshot, which `CLAUDE.md` \u2192 Git Rules forbids on your own judgement (`prompts/collaboration-workflow/operating-rules.md` \u2192 "no-self-staging"). The `.claude/settings.json` deny list matches prefix globs, so it misses `git -C . commit`, `git -c k=v commit` and `env git commit`; this row reads the argv instead, so the spelling does not matter. A commit goes through `pnpm josh git -y`; any other staging needs explicit current-turn user instruction, run by the user in their own terminal. **This rule fires on every occurrence, not once per run.**';
|
|
2251
|
+
var ROW15 = {
|
|
2080
2252
|
id: "index-mutation",
|
|
2081
2253
|
is_trigger: bash_triggers.on_bash_command(is_index_mutation),
|
|
2082
2254
|
reason: INDEX_MUTATION_REASON,
|
|
2083
2255
|
decide: () => true
|
|
2084
2256
|
};
|
|
2085
|
-
var index_guard = { INDEX_MUTATION_REASON, ROW:
|
|
2257
|
+
var index_guard = { INDEX_MUTATION_REASON, ROW: ROW15, is_index_mutation };
|
|
2086
2258
|
|
|
2087
2259
|
// scripts/rules/protected-files.ts
|
|
2088
2260
|
import path5 from "node:path";
|
|
@@ -2102,7 +2274,6 @@ var CLAUDE_MD = "CLAUDE.md";
|
|
|
2102
2274
|
var PLUGIN_ID = "kit@kit";
|
|
2103
2275
|
var CLAUDE_MD_POINTER = `@${NODE_MODULES}/${KIT_PACKAGE_NAME}/dist/${CLAUDE_MD}`;
|
|
2104
2276
|
var SECRETLINT_BIN = path4.join(NODE_MODULES, ".bin", "secretlint");
|
|
2105
|
-
var GIT_TIMEOUT_MS = 2e3;
|
|
2106
2277
|
var manifest_schema = z6.object({
|
|
2107
2278
|
name: z6.string().optional(),
|
|
2108
2279
|
dependencies: z6.record(z6.string(), z6.string()).optional(),
|
|
@@ -2137,7 +2308,7 @@ function is_plugin_declared(root) {
|
|
|
2137
2308
|
}
|
|
2138
2309
|
function configured_hooks_path(root) {
|
|
2139
2310
|
const value = git_spawn_sync.read(["-C", root, "config", "--get", "core.hooksPath"], {
|
|
2140
|
-
timeout:
|
|
2311
|
+
timeout: PROBE_TIMEOUT_MS
|
|
2141
2312
|
});
|
|
2142
2313
|
return value === "" ? void 0 : value;
|
|
2143
2314
|
}
|
|
@@ -2213,13 +2384,51 @@ var READ_TOOL = "Read";
|
|
|
2213
2384
|
var EDIT_TOOLS2 = /* @__PURE__ */ new Set(["Edit", "Write"]);
|
|
2214
2385
|
var ENVIRONMENT_FILE = ".env";
|
|
2215
2386
|
var SETTINGS_FILE = path5.join(".claude", "settings.json");
|
|
2387
|
+
var WHITESPACE3 = /\s+/u;
|
|
2388
|
+
var TOKEN_QUOTES = /["']/gu;
|
|
2389
|
+
var INPUT_REDIRECT = "<";
|
|
2390
|
+
var SHELL_READERS = /* @__PURE__ */ new Set([
|
|
2391
|
+
".",
|
|
2392
|
+
"awk",
|
|
2393
|
+
"bat",
|
|
2394
|
+
"cat",
|
|
2395
|
+
"cut",
|
|
2396
|
+
"grep",
|
|
2397
|
+
"head",
|
|
2398
|
+
"less",
|
|
2399
|
+
"more",
|
|
2400
|
+
"nl",
|
|
2401
|
+
"od",
|
|
2402
|
+
"rg",
|
|
2403
|
+
"sed",
|
|
2404
|
+
"sort",
|
|
2405
|
+
"source",
|
|
2406
|
+
"strings",
|
|
2407
|
+
"tail",
|
|
2408
|
+
"xxd"
|
|
2409
|
+
]);
|
|
2216
2410
|
function file_path_of(call) {
|
|
2217
2411
|
if (!json_value.is_record(call.input)) return "";
|
|
2218
2412
|
const value = call.input["file_path"];
|
|
2219
2413
|
return typeof value === "string" ? value : "";
|
|
2220
2414
|
}
|
|
2415
|
+
function is_environment_path(token) {
|
|
2416
|
+
return path5.basename(token.replaceAll(TOKEN_QUOTES, "")) === ENVIRONMENT_FILE;
|
|
2417
|
+
}
|
|
2418
|
+
function is_environment_read_segment(segment) {
|
|
2419
|
+
const words = segment.replaceAll(INPUT_REDIRECT, " < ").split(WHITESPACE3).filter((word) => word !== "");
|
|
2420
|
+
const is_redirected = words.some(
|
|
2421
|
+
(word, index) => word === INPUT_REDIRECT && is_environment_path(words[index + 1] ?? "")
|
|
2422
|
+
);
|
|
2423
|
+
const [command = "", ...rest] = words;
|
|
2424
|
+
return is_redirected || SHELL_READERS.has(command) && rest.some((word) => is_environment_path(word));
|
|
2425
|
+
}
|
|
2426
|
+
function is_environment_shell_read(command) {
|
|
2427
|
+
return shell_segments.segments_of(command).some((segment) => is_environment_read_segment(segment));
|
|
2428
|
+
}
|
|
2221
2429
|
function is_environment_read(call) {
|
|
2222
|
-
|
|
2430
|
+
if (call.name === READ_TOOL) return is_environment_path(file_path_of(call));
|
|
2431
|
+
return bash_triggers.on_bash_command(is_environment_shell_read)(call);
|
|
2223
2432
|
}
|
|
2224
2433
|
function is_consumer_settings_edit(call, is_kit_consumer2) {
|
|
2225
2434
|
if (!EDIT_TOOLS2.has(call.name)) return false;
|
|
@@ -2230,14 +2439,14 @@ function is_consumer_settings_edit(call, is_kit_consumer2) {
|
|
|
2230
2439
|
function is_protected_file_call(call, is_kit_consumer2 = doctor_consumer.is_kit_consumer) {
|
|
2231
2440
|
return is_environment_read(call) || is_consumer_settings_edit(call, is_kit_consumer2);
|
|
2232
2441
|
}
|
|
2233
|
-
var PROTECTED_FILE_REASON = '\u26D4 protected file: `.env` holds the Telegram bot token, so reading it puts a secret in the transcript \u2014 the scripts that need it load it in their own process. `.claude/settings.json` in a consumer repository is the deny list and the hooks every other rule rests on, so a run must not rewrite it; `josh sync` owns it, and a change belongs upstream in kit (`CLAUDE.md` \u2192 "Route distributed-doc / config changes upstream to kit"). Leave both files alone, or ask the user. **This rule fires on every occurrence, not once per run.**';
|
|
2234
|
-
var
|
|
2442
|
+
var PROTECTED_FILE_REASON = '\u26D4 protected file: `.env` holds the Telegram bot token, so reading it \u2014 with the Read tool or a shell reader such as `cat` / `grep` / `source` \u2014 puts a secret in the transcript \u2014 the scripts that need it load it in their own process. `.claude/settings.json` in a consumer repository is the deny list and the hooks every other rule rests on, so a run must not rewrite it; `josh sync` owns it, and a change belongs upstream in kit (`CLAUDE.md` \u2192 "Route distributed-doc / config changes upstream to kit"). Leave both files alone, or ask the user. **This rule fires on every occurrence, not once per run.**';
|
|
2443
|
+
var ROW16 = {
|
|
2235
2444
|
id: "protected-file",
|
|
2236
2445
|
is_trigger: is_protected_file_call,
|
|
2237
2446
|
reason: PROTECTED_FILE_REASON,
|
|
2238
2447
|
decide: () => true
|
|
2239
2448
|
};
|
|
2240
|
-
var protected_files = { ROW:
|
|
2449
|
+
var protected_files = { ROW: ROW16, is_protected_file_call };
|
|
2241
2450
|
|
|
2242
2451
|
// scripts/rules/permission-guards.ts
|
|
2243
2452
|
var ROWS2 = [
|
|
@@ -2316,7 +2525,7 @@ function is_poll_loop(command) {
|
|
|
2316
2525
|
return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain);
|
|
2317
2526
|
}
|
|
2318
2527
|
var POLL_LOOP_REASON = '\u26D4 output poll loop: this hand-writes a `while`/`until` wait loop over a command\'s output, a marker file or a process \u2014 with a `sleep` or, worse, without one \u2014 and the loop does not know when the command finished. A regex that never matches waits to the harness block limit; one that matches a mid-run line exits too early \u2014 either way the command exit is not what ends the wait, and each pass spends a full-context round trip (joshuafolkken/kit#2371 measured about 45 minutes, 13% of six lanes, lost to exactly this). Do not poll the output file. A command known to take minutes \u2014 `pnpm josh gate`, `pnpm josh followup`, `pnpm josh format` \u2014 is started detached from the first with `run_in_background` set, and the harness re-invokes you when it exits: its completion notification is what ends the wait, on the task exit rather than a regex or the block limit. So issue the long command in the background and end the turn \u2014 do not arm a loop; the wake-up brings you back. To glance at interim output, `Read` the output file once, never in a sleep loop. If you are waiting on CI, `pnpm josh followup` waits on it for you. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every occurrence, not once per run.**';
|
|
2319
|
-
var
|
|
2528
|
+
var ROW17 = {
|
|
2320
2529
|
id: "poll-loop",
|
|
2321
2530
|
is_trigger: bash_triggers.on_bash_command(is_poll_loop),
|
|
2322
2531
|
reason: POLL_LOOP_REASON,
|
|
@@ -2324,7 +2533,7 @@ var ROW15 = {
|
|
|
2324
2533
|
};
|
|
2325
2534
|
var poll_loop = {
|
|
2326
2535
|
POLL_LOOP_REASON,
|
|
2327
|
-
ROW:
|
|
2536
|
+
ROW: ROW17,
|
|
2328
2537
|
is_poll_loop
|
|
2329
2538
|
};
|
|
2330
2539
|
|
|
@@ -2388,69 +2597,19 @@ var pre_gate_cut = {
|
|
|
2388
2597
|
uncut_lane_issue
|
|
2389
2598
|
};
|
|
2390
2599
|
|
|
2391
|
-
// scripts/rules/prior-comment-read.ts
|
|
2392
|
-
var ISSUE_PATH_NUMBER = /issues\/(\d+)/gu;
|
|
2393
|
-
var ISSUE_VIEW_NUMBER = /\bissue\s+view\s+(?:-{1,2}\S+\s+)*(\d+)/u;
|
|
2394
|
-
var ISSUE_READ_ARGUMENTS = /\bissue:read\b([\s\d]*)/u;
|
|
2395
|
-
var A_NUMBER = /^\d+$/u;
|
|
2396
|
-
var COMMENTS_ENDPOINT = /issues\/\d+\/comments\b/u;
|
|
2397
|
-
var VIEW_WITH_COMMENTS = /\bissue\s+view\b[^\n]*--comments\b/u;
|
|
2398
|
-
var ISSUE_READ_COMMAND = /\bissue:read\b/u;
|
|
2399
|
-
function add_issue_read_numbers(command, numbers) {
|
|
2400
|
-
const match = ISSUE_READ_ARGUMENTS.exec(command);
|
|
2401
|
-
if (match === null) return;
|
|
2402
|
-
const tokens = (match[1] ?? "").split(/\s+/u);
|
|
2403
|
-
for (const token of tokens) {
|
|
2404
|
-
if (A_NUMBER.test(token)) numbers.add(Number(token));
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
function issue_numbers_of(command) {
|
|
2408
|
-
const numbers = /* @__PURE__ */ new Set();
|
|
2409
|
-
for (const match of command.matchAll(ISSUE_PATH_NUMBER)) numbers.add(Number(match[1]));
|
|
2410
|
-
const view = ISSUE_VIEW_NUMBER.exec(command);
|
|
2411
|
-
if (view !== null) numbers.add(Number(view[1]));
|
|
2412
|
-
add_issue_read_numbers(command, numbers);
|
|
2413
|
-
return numbers;
|
|
2414
|
-
}
|
|
2415
|
-
function fetches_comments(command) {
|
|
2416
|
-
return COMMENTS_ENDPOINT.test(command) || VIEW_WITH_COMMENTS.test(command) || ISSUE_READ_COMMAND.test(command);
|
|
2417
|
-
}
|
|
2418
|
-
function reads_target_comments(command, target) {
|
|
2419
|
-
if (!fetches_comments(command)) return false;
|
|
2420
|
-
for (const number of issue_numbers_of(command)) {
|
|
2421
|
-
if (target.has(number)) return true;
|
|
2422
|
-
}
|
|
2423
|
-
return false;
|
|
2424
|
-
}
|
|
2425
|
-
function already_read_comments_for(tail, command) {
|
|
2426
|
-
const target = issue_numbers_of(command);
|
|
2427
|
-
if (target.size === 0) return false;
|
|
2428
|
-
return tail_commands.prior_bash_commands(tail).some((prior) => reads_target_comments(prior, target));
|
|
2429
|
-
}
|
|
2430
|
-
function already_read_for_call(tail, call) {
|
|
2431
|
-
if (call.name !== cost_blocks.BASH_TOOL) return false;
|
|
2432
|
-
return already_read_comments_for(tail, time_shell.bash_command(call.input));
|
|
2433
|
-
}
|
|
2434
|
-
var prior_comment_read = {
|
|
2435
|
-
already_read_comments_for,
|
|
2436
|
-
already_read_for_call,
|
|
2437
|
-
fetches_comments,
|
|
2438
|
-
issue_numbers_of
|
|
2439
|
-
};
|
|
2440
|
-
|
|
2441
2600
|
// scripts/rules/raw-field-body.ts
|
|
2442
2601
|
var RAW_FIELD_BODY_AT = /(?:^|\s)(?:-f|--raw-field)[\s=]+['"]?body=['"]?@/u;
|
|
2443
2602
|
function posts_at_path_literally(command) {
|
|
2444
2603
|
return RAW_FIELD_BODY_AT.test(command);
|
|
2445
2604
|
}
|
|
2446
2605
|
var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which sends the value verbatim \u2014 the string `@<path>` is posted as the comment, not the file it points at. `-F` / `--field` is the flag that reads the file, and the two spellings differ by one character while `gh` exits 0 and returns a comment URL either way, so the break is invisible until a person reads the issue (joshuafolkken/kit#2304 lost two park comments this way). Post an issue or PR comment with `pnpm josh issue:comment <N> --body-file <path>` \u2014 the one spelling, which cannot take the wrong flag. The rule and the safe forms are in `prompts/collaboration-workflow/shell-body.md`. This rule fires on every occurrence, not once per run.";
|
|
2447
|
-
var
|
|
2606
|
+
var ROW18 = {
|
|
2448
2607
|
id: "raw-field-body",
|
|
2449
2608
|
is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
|
|
2450
2609
|
reason: RAW_FIELD_BODY_REASON,
|
|
2451
2610
|
decide: () => true
|
|
2452
2611
|
};
|
|
2453
|
-
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW:
|
|
2612
|
+
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW18, posts_at_path_literally };
|
|
2454
2613
|
|
|
2455
2614
|
// scripts/rules/rule-body-guard.ts
|
|
2456
2615
|
var EDIT_TOOL = "Edit";
|
|
@@ -2521,7 +2680,7 @@ function runs_placement_oracle(command) {
|
|
|
2521
2680
|
(names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
|
|
2522
2681
|
);
|
|
2523
2682
|
}
|
|
2524
|
-
var
|
|
2683
|
+
var ROW19 = {
|
|
2525
2684
|
id: "rule-body",
|
|
2526
2685
|
is_trigger: writes_rule_prose,
|
|
2527
2686
|
reason: RULE_BODY_REASON,
|
|
@@ -2530,7 +2689,7 @@ var ROW17 = {
|
|
|
2530
2689
|
};
|
|
2531
2690
|
var rule_body_guard = {
|
|
2532
2691
|
RULE_BODY_REASON,
|
|
2533
|
-
ROW:
|
|
2692
|
+
ROW: ROW19,
|
|
2534
2693
|
answered_placement,
|
|
2535
2694
|
is_rule_document,
|
|
2536
2695
|
writes_rule_prose
|
|
@@ -2761,13 +2920,10 @@ var TYPE_CONFIGS = ["tsconfig.json", "jsconfig.json"];
|
|
|
2761
2920
|
var DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
|
|
2762
2921
|
var object_schema = z7.record(z7.string(), z7.unknown());
|
|
2763
2922
|
function project_root(directory) {
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
current = parent;
|
|
2769
|
-
}
|
|
2770
|
-
return current;
|
|
2923
|
+
return ancestor_directories.nearest(
|
|
2924
|
+
directory,
|
|
2925
|
+
(candidate) => existsSync4(path7.join(candidate, PACKAGE_JSON))
|
|
2926
|
+
) ?? directory;
|
|
2771
2927
|
}
|
|
2772
2928
|
function is_ignored(entry) {
|
|
2773
2929
|
return entry.split(/[/\\]/u).some((part) => IGNORED_DIRS.has(part));
|
|
@@ -2961,12 +3117,12 @@ var test_declared_changed = { current_verdict, path_of, read_changed_paths_sync,
|
|
|
2961
3117
|
|
|
2962
3118
|
// scripts/rules/test-declared-commit.ts
|
|
2963
3119
|
function is_untested_commit(call) {
|
|
2964
|
-
if (!
|
|
3120
|
+
if (!run_tail_rule.is_push_step_call(call)) return false;
|
|
2965
3121
|
return test_declared_changed.current_verdict() === "required";
|
|
2966
3122
|
}
|
|
2967
3123
|
var REASON = '\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads the working tree as `required` (a changed file that is neither a test \u2014 `*.test.ts` / `*.e2e.ts` \u2014 nor a mechanically-exempt non-runtime path). `CLAUDE.md` \u2192 Code Change Rules requires a test for ALL code changes: a bug fix gets a regression test, a refactor gets tests pinning behavior first, logic gets a unit test, an observable UI/timing change gets an E2E. Add the declared test and reissue this commit. **If the change genuinely touches no executable runtime path** it is the pre-approved non-runtime exception \u2014 declare it in your Step 0 work summary with the reason it is exempt (non-executable config, a cosmetic asset swap) and reissue; the exception is a presentation, not a stop. Run `pnpm josh test:declared` to see the files it names. The procedure is `prompts/collaboration-workflow/rule-delivery.md` \u2192 "\u914D\u9001\u3055\u308C\u3066\u3044\u308B\u898F\u5247". This fires once per run, so reissuing will not repeat on the call in hand.';
|
|
2968
|
-
var
|
|
2969
|
-
var test_declared_commit = { REASON, ROW:
|
|
3124
|
+
var ROW20 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
|
|
3125
|
+
var test_declared_commit = { REASON, ROW: ROW20 };
|
|
2970
3126
|
|
|
2971
3127
|
// scripts/rules/third-party-write.ts
|
|
2972
3128
|
function is_third_party_write_segment(segment, session_owner, resolve_placeholder_owners) {
|
|
@@ -3004,50 +3160,21 @@ function writes_third_party(command, resolve_owner = repo_party.current_owner, r
|
|
|
3004
3160
|
);
|
|
3005
3161
|
}
|
|
3006
3162
|
var THIRD_PARTY_WRITE_REASON = '\u26D4 third-party write: this `gh api` call writes to a repository whose owner is not this session \u2014 a tracker we do not own is Tier C, and every write there (Issue, comment, PR, Discussion, review) needs explicit current-turn user instruction; typing the target, or holding a correct diagnosis, is not that instruction (`CLAUDE.md` \u2192 "Third-party repositories are Tier C", `prompts/collaboration-workflow/upstream-interrupt.md`). Confirm the party with `pnpm josh repo:party <owner/repo>` \u2014 owner equality, computed not judged. A read (`-X GET`, or no field flag) passes untouched; only a write (`-f`/`-F`/`--field`/`--raw-field`/`--input`, or `-X POST|PATCH|PUT|DELETE`) is refused. Record the finding on our own Issue and draft the artifact for a person to post \u2014 do not publish it yourself. **This rule fires on every occurrence, not once per run.**';
|
|
3007
|
-
var
|
|
3163
|
+
var ROW21 = {
|
|
3008
3164
|
id: "third-party-write",
|
|
3009
3165
|
is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
|
|
3010
3166
|
reason: THIRD_PARTY_WRITE_REASON,
|
|
3011
3167
|
decide: () => true
|
|
3012
3168
|
};
|
|
3013
3169
|
var third_party_write = {
|
|
3014
|
-
ROW:
|
|
3170
|
+
ROW: ROW21,
|
|
3015
3171
|
THIRD_PARTY_WRITE_REASON,
|
|
3016
3172
|
writes_third_party
|
|
3017
3173
|
};
|
|
3018
3174
|
|
|
3019
|
-
// scripts/rules/
|
|
3020
|
-
var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
|
|
3021
|
-
var STAMP_PREFIX = "josh-rule-guard-";
|
|
3022
|
-
var { is_issue_filing, on_bash_command } = bash_triggers;
|
|
3175
|
+
// scripts/rules/wip-cap.ts
|
|
3023
3176
|
var WIP_CAP = 30;
|
|
3024
3177
|
var WIP_CAP_REASON = `\u26D4 backlog WIP cap: \`pnpm josh issue:file\` counts the target repository's open Issues before filing. With more than ${String(WIP_CAP)} open, close one first; nothing honestly closable means do not file \u2014 the command holds such a filing. Two filings are exempt and proceed while stating the overage \u2014 one the run is blocked by (\`--over-cap\`, or the \`tier-a\` / \`split\` route), and an interrupt (\`--route interrupt\`), decided by three tests rather than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost or written outside the repository. Meeting none of the three, the finding is discretionary and waits. Both procedures are in \`prompts/collaboration-workflow/wip-cap.md\`. Reissue this call and let the command count \u2014 it fires once per run and cannot repeat on the call in hand.`;
|
|
3025
|
-
var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
|
|
3026
|
-
var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
|
|
3027
|
-
var FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u;
|
|
3028
|
-
var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
|
|
3029
|
-
var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
|
|
3030
|
-
var NAMES_THE_BODY = /\bbody\b/u;
|
|
3031
|
-
function projects_away_body(segment) {
|
|
3032
|
-
const projection = FIELD_PROJECTION.exec(segment);
|
|
3033
|
-
return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? "");
|
|
3034
|
-
}
|
|
3035
|
-
function is_body_read_segment(segment) {
|
|
3036
|
-
if (projects_away_body(segment)) return false;
|
|
3037
|
-
if (ISSUE_VIEW_COMMAND.test(segment)) return true;
|
|
3038
|
-
return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
|
|
3039
|
-
}
|
|
3040
|
-
function fetches_issue_comments(segment) {
|
|
3041
|
-
if (!FETCHES_COMMENTS.test(segment)) return false;
|
|
3042
|
-
return ISSUE_VIEW_COMMAND.test(segment) || gh_api.is_gh_api(segment) && ISSUES_PATH.test(segment);
|
|
3043
|
-
}
|
|
3044
|
-
function is_body_only_issue_read(command) {
|
|
3045
|
-
const segments = shell_segments.segments_of(command);
|
|
3046
|
-
if (segments.some((segment) => fetches_issue_comments(segment))) return false;
|
|
3047
|
-
return segments.some((segment) => is_body_read_segment(segment));
|
|
3048
|
-
}
|
|
3049
|
-
var ISSUE_COMMENTS_REASON = "\u26D4 an Issue's comments are part of the Issue: read them before implementing, not only the body. A decision recorded after the body was written lives only in a comment \u2014 a corrected diagnosis (joshuafolkken/kit#1537), a changed default and an added acceptance criterion (joshuafolkken/kit#1520), a scope handed to another Issue (joshuafolkken/kit#1304) \u2014 and nothing in the body says it was superseded, so a body-only reader builds the wrong thing and sees no contradiction. Reissue this read with the comments included: `gh api repos/{owner}/{repo}/issues/<N>/comments --jq '.[] | {user: .user.login, created_at, body}'` beside the body read, or `gh issue view <N> --comments` where GraphQL is reachable. Then the later text is the agreement in force \u2014 a comment supersedes the body it contradicts \u2014 except for two answers that are not the run's to make: work a comment reassigns to another Issue is out of scope and is not implemented, and a comment saying the Issue no longer has a reason to exist stops the run with a `confirmation` Telegram. The procedure is `.claude/skills/workflow-commands/issue-comments.md`. Every body-only read is refused until the comments are read.";
|
|
3050
|
-
var { SHELL_BODY_REASON: SHELL_BODY_REASON2, carries_a_body: carries_a_body2, is_shell_evaluated_body: is_shell_evaluated_body2, keeps_body_safe: keeps_body_safe2 } = shell_body_trigger;
|
|
3051
3178
|
var ISSUE_LISTING = /^gh\s+(?:-{1,2}[\w-]+(?:[= ]\S+)?\s+)*issue\s+list\b/u;
|
|
3052
3179
|
var ISSUES_QUERY = /repos\/[^\s'"]*\/issues\?[^\s'"]*state=open/u;
|
|
3053
3180
|
var OPEN_STATE = /--state[= ]open|state=open/u;
|
|
@@ -3058,9 +3185,19 @@ function counts_open_issues(command) {
|
|
|
3058
3185
|
return ISSUE_LISTING.test(segment) && OPEN_STATE.test(segment) || ISSUES_QUERY.test(segment);
|
|
3059
3186
|
});
|
|
3060
3187
|
}
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3188
|
+
var ROW22 = {
|
|
3189
|
+
id: "wip-cap",
|
|
3190
|
+
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
3191
|
+
reason: WIP_CAP_REASON,
|
|
3192
|
+
keeps: bash_triggers.on_bash_command(counts_open_issues)
|
|
3193
|
+
};
|
|
3194
|
+
var wip_cap = { ROW: ROW22, WIP_CAP, WIP_CAP_REASON };
|
|
3195
|
+
|
|
3196
|
+
// scripts/rules/delivered-rules.ts
|
|
3197
|
+
var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
|
|
3198
|
+
var STAMP_PREFIX = "josh-rule-guard-";
|
|
3199
|
+
var { is_issue_filing, on_bash_command } = bash_triggers;
|
|
3200
|
+
var { SHELL_BODY_REASON: SHELL_BODY_REASON2, carries_a_body: carries_a_body2, is_shell_evaluated_body: is_shell_evaluated_body2, keeps_body_safe: keeps_body_safe2 } = shell_body_trigger;
|
|
3064
3201
|
var DELIVERED_RULES = [
|
|
3065
3202
|
// **Listed first, ahead of the filing trigger it overlaps** (joshuafolkken/kit#2122). A `gh api`
|
|
3066
3203
|
// that files an Issue into a repository we do not own trips both this row and `wip-cap` /
|
|
@@ -3075,12 +3212,7 @@ var DELIVERED_RULES = [
|
|
|
3075
3212
|
// `epic:bundle` itself. It claims the direct call only, so the rows below — which trigger on the
|
|
3076
3213
|
// `issue:file` call — never overlap it.
|
|
3077
3214
|
direct_filing.ROW,
|
|
3078
|
-
|
|
3079
|
-
id: "wip-cap",
|
|
3080
|
-
is_trigger: on_bash_command(is_issue_filing),
|
|
3081
|
-
reason: WIP_CAP_REASON,
|
|
3082
|
-
keeps: on_bash_command(counts_open_issues)
|
|
3083
|
-
},
|
|
3215
|
+
wip_cap.ROW,
|
|
3084
3216
|
// **Two more rows share the filing trigger** (joshuafolkken/kit#2119, joshuafolkken/kit#2213), listed
|
|
3085
3217
|
// after `wip-cap` so the backlog count still speaks first: `filing-cap` refuses every filing past
|
|
3086
3218
|
// the per-run ceiling, and `issue-fold` refuses a *second* filing the run has not folded. Each is a
|
|
@@ -3088,14 +3220,8 @@ var DELIVERED_RULES = [
|
|
|
3088
3220
|
// delivery is still correct one reissue later — so an unfolded, over-cap second filing is delivered
|
|
3089
3221
|
// `wip-cap`, then `filing-cap`, then `issue-fold` across its reissues.
|
|
3090
3222
|
filing_cap.ROW,
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
id: "issue-comments",
|
|
3094
|
-
is_trigger: on_bash_command(is_body_only_issue_read),
|
|
3095
|
-
reason: ISSUE_COMMENTS_REASON,
|
|
3096
|
-
already_satisfied: prior_comment_read.already_read_for_call,
|
|
3097
|
-
keeps: on_bash_command(reads_issue_comments)
|
|
3098
|
-
},
|
|
3223
|
+
issue_fold_rule.ROW,
|
|
3224
|
+
issue_comments.ROW,
|
|
3099
3225
|
{
|
|
3100
3226
|
id: "shell-body",
|
|
3101
3227
|
is_trigger: on_bash_command(is_shell_evaluated_body2),
|
|
@@ -3150,11 +3276,11 @@ var DELIVERED_RULES = [
|
|
|
3150
3276
|
// second inside one `fullrun` when round 2 fixes a finding in place.
|
|
3151
3277
|
{
|
|
3152
3278
|
id: "run-tail",
|
|
3153
|
-
is_trigger:
|
|
3154
|
-
reason:
|
|
3155
|
-
decide:
|
|
3156
|
-
keeps:
|
|
3157
|
-
reaches:
|
|
3279
|
+
is_trigger: run_tail_rule.is_foreground_push_step,
|
|
3280
|
+
reason: run_tail_rule.RUN_TAIL_REASON,
|
|
3281
|
+
decide: run_tail_rule.decide,
|
|
3282
|
+
keeps: run_tail_rule.is_backgrounded_push_step,
|
|
3283
|
+
reaches: run_tail_rule.is_push_step_call
|
|
3158
3284
|
},
|
|
3159
3285
|
// **The one row whose trigger consults the world beside the command**, because "is this a lane that
|
|
3160
3286
|
// has not cut" is not readable from the call: it is the working directory's own name and the cut
|
|
@@ -3197,22 +3323,14 @@ var DELIVERED_RULES = [
|
|
|
3197
3323
|
// "is this a dispatched lane child" is the dispatch mark against this checkout's own issue, read
|
|
3198
3324
|
// synchronously by `lane_child_marker.is_child_of`, and the command match runs first so an ordinary
|
|
3199
3325
|
// run pays nothing for it.
|
|
3326
|
+
// Its once-per-run disposition is stated beside the row in `lane-park.ts`.
|
|
3200
3327
|
//
|
|
3201
|
-
// **
|
|
3202
|
-
//
|
|
3203
|
-
//
|
|
3204
|
-
//
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
// path is the safe direction. `keeps` reads an actual label-application `Bash` command, never the
|
|
3208
|
-
// tail, so it is not fooled the same way; `reaches` stays `is_trigger`, since a compliant run issues
|
|
3209
|
-
// the same confirmation notify and belongs in the denominator.
|
|
3210
|
-
{
|
|
3211
|
-
id: "lane-park",
|
|
3212
|
-
is_trigger: on_bash_command(lane_park.is_unparked_stop),
|
|
3213
|
-
reason: lane_park.LANE_PARK_REASON,
|
|
3214
|
-
keeps: on_bash_command(lane_park.records_the_park)
|
|
3215
|
-
},
|
|
3328
|
+
// **A split child's park is refused first** (joshuafolkken/kit#3296): a lane child that promoted its
|
|
3329
|
+
// own Issue to an epic this run has nothing to park, so `lane-split-park` refuses the label write
|
|
3330
|
+
// and the confirmation notify onto that epic on every occurrence. It shares the notify with
|
|
3331
|
+
// `lane-park` and is listed first; a run that promoted nothing falls through to `lane-park`.
|
|
3332
|
+
lane_split_park.ROW,
|
|
3333
|
+
lane_park.ROW,
|
|
3216
3334
|
// **joshuafolkken/kit#2034's rule, one tool-call earlier** (joshuafolkken/kit#2201). `lane-park`
|
|
3217
3335
|
// fires on the `confirmation` notify a routed child reaches; a child that reaches for
|
|
3218
3336
|
// `AskUserQuestion` never gets there, because the harness ends its turn at the refused ask. This
|
|
@@ -3354,12 +3472,12 @@ function is_first_delivery(tail, call, delivered_at_ms, run2) {
|
|
|
3354
3472
|
return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run2);
|
|
3355
3473
|
}
|
|
3356
3474
|
function delivery_decision(rule) {
|
|
3357
|
-
const { decide:
|
|
3358
|
-
if (
|
|
3475
|
+
const { decide: decide7, already_satisfied } = rule;
|
|
3476
|
+
if (decide7 === void 0 && already_satisfied === void 0) return is_first_delivery;
|
|
3359
3477
|
return function should_block(tail, call, delivered_at_ms, run2) {
|
|
3360
3478
|
if (already_satisfied?.(tail, call) === true) return false;
|
|
3361
|
-
if (
|
|
3362
|
-
return
|
|
3479
|
+
if (decide7 === void 0) return true;
|
|
3480
|
+
return decide7(call, run2, !will_batch_guard_refuse(tail, call, run2), delivered_at_ms);
|
|
3363
3481
|
};
|
|
3364
3482
|
}
|
|
3365
3483
|
function guard_of(rule) {
|
|
@@ -3392,8 +3510,8 @@ var delivered_rules = {
|
|
|
3392
3510
|
DIRECT_FILING_REASON: direct_filing.DIRECT_FILING_REASON,
|
|
3393
3511
|
FILING_CAP_REASON: filing_cap.FILING_CAP_REASON,
|
|
3394
3512
|
GIT_FORCE_REASON: git_force.GIT_FORCE_REASON,
|
|
3395
|
-
ISSUE_COMMENTS_REASON,
|
|
3396
|
-
ISSUE_FOLD_REASON:
|
|
3513
|
+
ISSUE_COMMENTS_REASON: issue_comments.ISSUE_COMMENTS_REASON,
|
|
3514
|
+
ISSUE_FOLD_REASON: issue_fold_rule.ISSUE_FOLD_REASON,
|
|
3397
3515
|
LANE_INTERACTIVE_ASK_REASON: lane_interactive_ask.LANE_INTERACTIVE_ASK_REASON,
|
|
3398
3516
|
LANE_PARK_REASON: lane_park.LANE_PARK_REASON,
|
|
3399
3517
|
MEASURED_RULES,
|
|
@@ -3401,24 +3519,21 @@ var delivered_rules = {
|
|
|
3401
3519
|
POLL_LOOP_REASON: poll_loop.POLL_LOOP_REASON,
|
|
3402
3520
|
PRE_GATE_CUT_REASON: pre_gate_cut.PRE_GATE_CUT_REASON,
|
|
3403
3521
|
RAW_FIELD_BODY_REASON: raw_field_body.RAW_FIELD_BODY_REASON,
|
|
3404
|
-
RUN_TAIL_REASON:
|
|
3522
|
+
RUN_TAIL_REASON: run_tail_rule.RUN_TAIL_REASON,
|
|
3405
3523
|
SHELL_BODY_REASON: SHELL_BODY_REASON2,
|
|
3406
3524
|
SWITCH_ENV_KEY,
|
|
3407
3525
|
THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
|
|
3408
|
-
WIP_CAP,
|
|
3409
|
-
WIP_CAP_REASON,
|
|
3526
|
+
WIP_CAP: wip_cap.WIP_CAP,
|
|
3527
|
+
WIP_CAP_REASON: wip_cap.WIP_CAP_REASON,
|
|
3410
3528
|
WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
|
|
3411
3529
|
delivery,
|
|
3412
3530
|
delivery_path,
|
|
3413
|
-
is_body_only_issue_read,
|
|
3531
|
+
is_body_only_issue_read: issue_comments.is_body_only_issue_read,
|
|
3414
3532
|
is_enabled,
|
|
3415
3533
|
is_issue_filing
|
|
3416
3534
|
};
|
|
3417
3535
|
|
|
3418
|
-
// scripts/run/run-
|
|
3419
|
-
import { z as z12 } from "zod";
|
|
3420
|
-
|
|
3421
|
-
// scripts/run/run-progress-read.ts
|
|
3536
|
+
// scripts/run/progress/run-progress-read.ts
|
|
3422
3537
|
import { loadavg } from "node:os";
|
|
3423
3538
|
|
|
3424
3539
|
// scripts/epic/epic-busy.ts
|
|
@@ -3766,7 +3881,7 @@ function to_advice(verdict, has_work_to_stash) {
|
|
|
3766
3881
|
if (verdict === UNDETERMINED_VERDICT) return UNDETERMINED_ADVICE;
|
|
3767
3882
|
return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
|
|
3768
3883
|
}
|
|
3769
|
-
function
|
|
3884
|
+
function decide5(traces) {
|
|
3770
3885
|
const verdict = to_verdict(traces);
|
|
3771
3886
|
const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
|
|
3772
3887
|
return {
|
|
@@ -3865,11 +3980,11 @@ async function read_traces(request) {
|
|
|
3865
3980
|
}
|
|
3866
3981
|
async function check(request) {
|
|
3867
3982
|
run_issue_number.require_issue_number(request.issue);
|
|
3868
|
-
return
|
|
3983
|
+
return decide5(await read_traces(request));
|
|
3869
3984
|
}
|
|
3870
3985
|
var run_liveness = {
|
|
3871
3986
|
check,
|
|
3872
|
-
decide:
|
|
3987
|
+
decide: decide5,
|
|
3873
3988
|
describe_agent_state,
|
|
3874
3989
|
read_child_settled,
|
|
3875
3990
|
read_output_frozen,
|
|
@@ -4019,7 +4134,7 @@ function to_advice2(verdict, issue, tree) {
|
|
|
4019
4134
|
if (verdict === PARK_VERDICT) return PARK_ADVICE;
|
|
4020
4135
|
return CLEAN_ADVICE;
|
|
4021
4136
|
}
|
|
4022
|
-
function
|
|
4137
|
+
function decide6(tree, child, issue) {
|
|
4023
4138
|
const verdict = to_verdict2(tree, child);
|
|
4024
4139
|
return {
|
|
4025
4140
|
advice: to_advice2(verdict, issue, tree),
|
|
@@ -4120,8 +4235,8 @@ async function read_child_state(issue) {
|
|
|
4120
4235
|
async function check2(issue) {
|
|
4121
4236
|
require_issue_number(issue);
|
|
4122
4237
|
const tree = await read_tree_state();
|
|
4123
|
-
if (needs_reclaim(tree)) return
|
|
4124
|
-
return
|
|
4238
|
+
if (needs_reclaim(tree)) return decide6(tree, NO_CHILD_WORK, issue);
|
|
4239
|
+
return decide6(tree, await read_child_state(issue), issue);
|
|
4125
4240
|
}
|
|
4126
4241
|
var run_preflight = {
|
|
4127
4242
|
CLEAN_VERDICT,
|
|
@@ -4129,13 +4244,13 @@ var run_preflight = {
|
|
|
4129
4244
|
MERGED_PR,
|
|
4130
4245
|
STASH_LABEL_PREFIX,
|
|
4131
4246
|
check: check2,
|
|
4132
|
-
decide:
|
|
4247
|
+
decide: decide6,
|
|
4133
4248
|
read_child_state,
|
|
4134
4249
|
read_pr_state,
|
|
4135
4250
|
to_pr_state
|
|
4136
4251
|
};
|
|
4137
4252
|
|
|
4138
|
-
// scripts/run/run-progress-read.ts
|
|
4253
|
+
// scripts/run/progress/run-progress-read.ts
|
|
4139
4254
|
var { mark: mark2, parse_stamp: parse_stamp2, read_last_report: read_last_report2 } = run_progress_clock;
|
|
4140
4255
|
var FIRST_LOAD_AVERAGE = 0;
|
|
4141
4256
|
var IDLE_READ = { kind: "idle" };
|
|
@@ -4226,6 +4341,9 @@ var run_progress_read = {
|
|
|
4226
4341
|
stamp_target
|
|
4227
4342
|
};
|
|
4228
4343
|
|
|
4344
|
+
// scripts/run/run-watcher-hook.ts
|
|
4345
|
+
import { z as z12 } from "zod";
|
|
4346
|
+
|
|
4229
4347
|
// scripts/run/run-watcher-guard.ts
|
|
4230
4348
|
var WATCHER_TICK_MS = 3e4;
|
|
4231
4349
|
var TICK_MULTIPLIER = 3;
|
|
@@ -4235,7 +4353,7 @@ var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND3;
|
|
|
4235
4353
|
var STALE_NOTE = `Lane children are in-flight but the progress watcher has not pinged in the last ${String(STALE_THRESHOLD_S)}s \u2014 run \`pnpm josh run:progress --wait\` in the background before proceeding. It reports to the run event stream on its own and exits only when newly runnable work arrives, so start it once and relay nothing`;
|
|
4236
4354
|
var OK_RESULT = { kind: "ok" };
|
|
4237
4355
|
async function owes_watcher() {
|
|
4238
|
-
if (!await run_headless.is_backlog_parent()) return false;
|
|
4356
|
+
if (run_headless.is_headless() || !await run_headless.is_backlog_parent()) return false;
|
|
4239
4357
|
return await lane_registry.has_lanes_in_flight();
|
|
4240
4358
|
}
|
|
4241
4359
|
async function check3(life_target) {
|
|
@@ -4354,7 +4472,7 @@ function is_runtime_file(file_path, root) {
|
|
|
4354
4472
|
if (relative.startsWith(PARENT_PREFIX) || path9.isAbsolute(relative)) return false;
|
|
4355
4473
|
return !test_declared_logic.is_exempt(relative.split(path9.sep).join("/"));
|
|
4356
4474
|
}
|
|
4357
|
-
function
|
|
4475
|
+
function is_candidate2(raw_payload, root) {
|
|
4358
4476
|
const payload = hook_decision.parse_hook_payload(raw_payload);
|
|
4359
4477
|
if (payload === void 0 || !EDIT_TOOLS3.has(payload.tool_name)) return void 0;
|
|
4360
4478
|
const has_runtime_file = edited_paths(payload.tool_input).some(
|
|
@@ -4363,7 +4481,7 @@ function is_candidate(raw_payload, root) {
|
|
|
4363
4481
|
return has_runtime_file ? payload.transcript_path : void 0;
|
|
4364
4482
|
}
|
|
4365
4483
|
function notice(raw_payload, root = process.cwd()) {
|
|
4366
|
-
const transcript =
|
|
4484
|
+
const transcript = is_candidate2(raw_payload, root);
|
|
4367
4485
|
if (transcript === void 0) return void 0;
|
|
4368
4486
|
const target = STAMP.path(transcript);
|
|
4369
4487
|
if (STAMP.last_ms(target) !== hook_decision.NEVER_MS) return void 0;
|