@joshuafolkken/kit 1.1455.0 → 1.1514.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/settings.json +6 -6
- package/.claude/skills/dependency-update/SKILL.md +26 -55
- package/.claude/skills/epic-commands/SKILL.md +83 -156
- package/.claude/skills/epic-commands/epic-bundle.md +51 -79
- package/.claude/skills/epic-commands/execution-waves.md +43 -81
- package/.claude/skills/verify-ui/SKILL.md +24 -50
- package/.claude/skills/workflow-commands/SKILL.md +7 -9
- package/.claude/skills/workflow-commands/background-commands.md +31 -46
- package/.claude/skills/workflow-commands/backlogrun-child.md +65 -85
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +86 -125
- package/.claude/skills/workflow-commands/backlogrun-park.md +68 -118
- package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -28
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +17 -26
- package/.claude/skills/workflow-commands/backlogrun-steps.md +35 -52
- package/.claude/skills/workflow-commands/backlogrun.md +31 -50
- package/.claude/skills/workflow-commands/chain-rule.md +31 -40
- package/.claude/skills/workflow-commands/delegation.md +12 -8
- package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
- package/.claude/skills/workflow-commands/followup.md +55 -77
- package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
- package/.claude/skills/workflow-commands/fullrun.md +4 -6
- package/.claude/skills/workflow-commands/halfrun.md +3 -7
- package/.claude/skills/workflow-commands/into-target.md +1 -2
- package/.claude/skills/workflow-commands/issue-comments.md +43 -60
- package/.claude/skills/workflow-commands/issue-scout.md +3 -2
- package/.claude/skills/workflow-commands/kickoff.md +65 -74
- package/.claude/skills/workflow-commands/latest-gate.md +2 -6
- package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
- package/.claude/skills/workflow-commands/observation-filing.md +59 -118
- package/.claude/skills/workflow-commands/observation-ledger.md +52 -112
- package/.claude/skills/workflow-commands/pre-gate-cut.md +88 -142
- package/.claude/skills/workflow-commands/prerequisite.md +11 -17
- package/.claude/skills/workflow-commands/progress-watcher.md +23 -33
- package/.claude/skills/workflow-commands/prrun.md +3 -5
- package/.claude/skills/workflow-commands/retrospective.md +35 -54
- package/.claude/skills/workflow-commands/split-assessment.md +32 -58
- package/.claude/skills/workflow-commands/target-repository.md +1 -2
- package/.claude/skills/workflow-commands/working-tree-hold.md +54 -73
- package/.codex/hooks.json +6 -6
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/publish.yml +2 -2
- package/CLAUDE.md +7 -6
- package/cspell/index.yaml +7 -0
- package/dist/CLAUDE.md +7 -6
- package/dist/commands/document-section-cli.js +67 -34
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/chunk-4KKD2UTK.js +66 -0
- package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-6TS2JD6D.js} +14 -3
- package/dist/hooks/chunk-7DSBJVFH.js +50 -0
- package/dist/hooks/{chunk-5H4SSDSD.js → chunk-A65Y4YOR.js} +3 -3
- package/dist/hooks/{chunk-3C4XRYG6.js → chunk-APUO6U4J.js} +102 -19
- package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
- package/dist/hooks/chunk-GP4IEC4O.js +20 -0
- package/dist/hooks/chunk-JP45SOCJ.js +56 -0
- package/dist/hooks/{chunk-OLMO72ON.js → chunk-LAOV7NLM.js} +1 -1
- package/dist/hooks/chunk-M4HGZV7U.js +83 -0
- package/dist/hooks/{chunk-TBJFTSUY.js → chunk-O6GDRNJB.js} +9 -7
- package/dist/hooks/{chunk-HFGWYGUV.js → chunk-PF2NZL2A.js} +5 -5
- package/dist/hooks/{chunk-WNLVGT7E.js → chunk-PTICX4OL.js} +4 -5
- package/dist/hooks/{chunk-HHI6FO3R.js → chunk-RT27QRJW.js} +763 -1515
- package/dist/hooks/{chunk-7WQBPLUN.js → chunk-SYOTDYCI.js} +1149 -1095
- package/dist/hooks/{chunk-LIAFCXL7.js → chunk-TNUMY6NL.js} +1519 -1332
- package/dist/hooks/chunk-UPUZF3AN.js +279 -0
- package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-WK6SMBIC.js} +1932 -784
- package/dist/hooks/codex-hook-adapter.js +17 -14
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +5 -4
- package/dist/hooks/inputs.json +302 -247
- package/dist/hooks/investigation-guard.js +8 -11
- package/dist/hooks/pretool-guard.js +16 -13
- package/dist/hooks/session-lang.js +372 -26
- package/dist/hooks/stop-guard.js +2593 -189
- package/dist/josh.js +2128 -282
- package/dist/managed-marker/index.js +1 -2
- package/dist/version/index.js +15 -12
- package/eslint/base.js +2 -1
- package/eslint/rules/unicorn.js +16 -0
- package/lefthook/base.yml +5 -3
- package/package.json +14 -12
- package/prompts/coding-standards.md +12 -22
- package/prompts/collaboration-workflow/file-edits.md +25 -45
- package/prompts/collaboration-workflow/gh-rest.md +19 -36
- package/prompts/collaboration-workflow/glossary.md +5 -7
- package/prompts/collaboration-workflow/issue-citation.md +6 -14
- package/prompts/collaboration-workflow/issue-template.md +40 -32
- package/prompts/collaboration-workflow/lane-instructions.md +35 -0
- package/prompts/collaboration-workflow/operating-rules.md +33 -46
- package/prompts/collaboration-workflow/output-bounds.md +15 -15
- package/prompts/collaboration-workflow/overview.md +14 -19
- package/prompts/collaboration-workflow/plan-comment.md +13 -13
- package/prompts/collaboration-workflow/principles.md +57 -54
- package/prompts/collaboration-workflow/proposal-request.md +1 -9
- package/prompts/collaboration-workflow/report-format.md +41 -49
- package/prompts/collaboration-workflow/residency.md +15 -11
- package/prompts/collaboration-workflow/rule-delivery.md +7 -15
- package/prompts/collaboration-workflow/shell-body.md +10 -20
- package/prompts/collaboration-workflow/turn-batching.md +8 -14
- package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
- package/prompts/collaboration-workflow/wip-cap.md +26 -32
- package/prompts/collaboration-workflow.md +25 -25
- package/prompts/refactoring.md +33 -43
- package/prompts/review-rubric.md +84 -129
- package/prompts/review.md +110 -141
- package/prompts/sonar-hotspot-handling.md +22 -50
- package/prompts/testing-guide.md +48 -99
- package/scripts/adopt/adopt-toolkits.ts +2 -4
- package/scripts/agent/agent-argv.ts +20 -4
- package/scripts/agent/agent-exit-record.ts +23 -1
- package/scripts/agent/agent-role-profile.ts +101 -45
- package/scripts/agent/agent-safe-text.ts +60 -0
- package/scripts/agent/claude-agent-argv.ts +14 -0
- package/scripts/agent/codex-agent-argv.ts +9 -2
- package/scripts/agent/lane-instructions.ts +144 -0
- package/scripts/backlog/backlog-budget-cli.ts +1 -2
- package/scripts/backlog/backlog-budget.ts +4 -4
- package/scripts/backlog/backlog-drive-carried.ts +26 -0
- package/scripts/backlog/backlog-drive-cli.ts +10 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -5
- package/scripts/backlog/backlog-drive-restore.ts +28 -2
- package/scripts/backlog/backlog-drive-seats.ts +27 -0
- package/scripts/backlog/backlog-drive.ts +11 -6
- package/scripts/backlog/backlog-occupied.ts +56 -0
- package/scripts/backlog/backlog-ready.ts +59 -15
- package/scripts/behavior/behavior-rules.ts +3 -2
- package/scripts/cost-runtime/codex-usage.ts +4 -2
- package/scripts/cost-runtime/cost-cli.ts +1 -2
- package/scripts/delegation/duplicate-read-guard.ts +2 -2
- package/scripts/delegation/investigation-guard.ts +15 -65
- package/scripts/delegation/investigation-reads.ts +1 -2
- package/scripts/discovery/repo-discovery.ts +4 -2
- package/scripts/discovery/repo-origin.ts +2 -4
- package/scripts/doctor/doctor-logic.ts +6 -3
- package/scripts/document/document-byte-budget.ts +28 -27
- package/scripts/document/document-byte-check.ts +1 -2
- package/scripts/document/document-section-cli.ts +17 -5
- package/scripts/document/entry-read-budget.ts +3 -3
- package/scripts/document/read-set-cli.ts +4 -1
- package/scripts/epic/epic-audit-orphans.ts +5 -8
- package/scripts/epic/epic-audit-rationale.ts +8 -2
- package/scripts/epic/epic-audit.ts +52 -1
- package/scripts/epic/epic-bundle-cli.ts +1 -2
- package/scripts/epic/epic-bundle-evidence.ts +49 -26
- package/scripts/epic/epic-bundle-referenced.ts +14 -3
- package/scripts/epic/epic-bundle.ts +17 -9
- package/scripts/epic/epic-chains.ts +6 -2
- package/scripts/epic/epic-close-comment.ts +1 -2
- package/scripts/followup/git-followup-workflow.ts +19 -1
- package/scripts/gate/check-inputs.ts +129 -0
- package/scripts/gate/check-reuse.ts +162 -0
- package/scripts/gate/core-budget.ts +93 -44
- package/scripts/gate/core-wait.ts +130 -0
- package/scripts/gate/free-cores.ts +49 -0
- package/scripts/gate/gate-ledger.ts +17 -3
- package/scripts/gate/gate-plan.ts +23 -1
- package/scripts/gate/gate-report.ts +15 -2
- package/scripts/gate/gate-run-steps.ts +150 -0
- package/scripts/gate/held-mark.ts +55 -0
- package/scripts/gate/machine-capacity.ts +70 -11
- package/scripts/gate/pre-commit-type-check.ts +14 -1
- package/scripts/gate/verification-gate.ts +65 -127
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +24 -2
- package/scripts/gh/git-pr.ts +19 -3
- package/scripts/git/git-common-directory.ts +3 -3
- package/scripts/git/git-diff-reads.ts +16 -9
- package/scripts/git/git-execa-error.ts +1 -2
- package/scripts/git/main-merge-regenerate.ts +78 -0
- package/scripts/git/main-merge.ts +14 -5
- package/scripts/hooks/batch-guard.ts +5 -8
- package/scripts/hooks/format-edited-file.ts +10 -4
- package/scripts/hooks/pre-commit-lint.ts +91 -0
- package/scripts/hooks/pre-push-unit.ts +15 -4
- package/scripts/hooks/pretool-guard.ts +48 -79
- package/scripts/hooks/reserved-run.ts +1 -0
- package/scripts/hooks/run-hook.sh +10 -12
- package/scripts/hooks/step-zero-notice.ts +3 -2
- package/scripts/hooks/stop-actions.ts +204 -0
- package/scripts/hooks/stop-guard.ts +62 -21
- package/scripts/init/hook-command-rewrite.ts +6 -2
- package/scripts/init/hook-launch.ts +12 -7
- package/scripts/init/init-logic-deploy-vps.ts +4 -2
- package/scripts/issue/behavior-change-lint.ts +12 -6
- package/scripts/issue/expected-effect-lint.ts +59 -0
- package/scripts/issue/issue-cite-cli.ts +35 -15
- package/scripts/issue/issue-classification.ts +1 -1
- package/scripts/issue/issue-lint-cli.ts +5 -3
- package/scripts/issue/issue-scout-cli.ts +22 -16
- package/scripts/issue/markdown-section.ts +19 -5
- package/scripts/josh/command-usage.ts +110 -0
- package/scripts/josh/file-map-stamp.ts +1 -3
- package/scripts/josh/hook-decision.ts +17 -2
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +55 -9
- package/scripts/josh/josh-commands-ai.ts +13 -0
- package/scripts/josh/josh-commands-development.ts +1 -1
- package/scripts/josh/josh-commands-hooks.ts +6 -0
- package/scripts/josh/josh-commands-lane.ts +10 -1
- package/scripts/josh/josh-commands-workflow.ts +5 -0
- package/scripts/josh/josh-logic.ts +6 -15
- package/scripts/josh/josh-usage-automation.ts +121 -0
- package/scripts/josh/josh-usage-backlog.ts +180 -0
- package/scripts/josh/josh-usage-developer.ts +230 -0
- package/scripts/josh/josh-usage-hooks.ts +38 -0
- package/scripts/josh/josh-usage-issue.ts +205 -0
- package/scripts/josh/josh-usage-review.ts +101 -0
- package/scripts/josh/josh-usage-run.ts +252 -0
- package/scripts/josh/josh-usage-session.ts +238 -0
- package/scripts/josh/josh-usage-workflow.ts +212 -0
- package/scripts/josh/josh.ts +14 -5
- package/scripts/josh/package-directory.ts +20 -0
- package/scripts/lane/lane-await.ts +3 -2
- package/scripts/lane/lane-capacity.ts +8 -2
- package/scripts/lane/lane-check-stats.ts +56 -0
- package/scripts/lane/lane-child-invocation.ts +49 -5
- package/scripts/lane/lane-dispatch.ts +3 -3
- package/scripts/lane/lane-guard-policy.ts +5 -5
- package/scripts/lane/lane-handoff.ts +8 -1
- package/scripts/lane/lane-install.ts +12 -4
- package/scripts/lane/lane-ledger.ts +52 -1
- package/scripts/lane/lane-limit-cli.ts +8 -9
- package/scripts/lane/lane-load.ts +9 -2
- package/scripts/lane/lane-measure-cli.ts +4 -0
- package/scripts/lane/lane-parked.ts +18 -0
- package/scripts/lane/lane-preserve.ts +29 -1
- package/scripts/lane/lane-process-load.ts +125 -0
- package/scripts/lane/lane-process-stats.ts +93 -0
- package/scripts/lane/lane-registry.ts +17 -10
- package/scripts/lane/lane-restart.ts +5 -26
- package/scripts/lane/lane-session-action.ts +146 -0
- package/scripts/lane/lane-session-broker.ts +68 -125
- package/scripts/lane/lane-spent.ts +14 -3
- package/scripts/lane/lane-stats.ts +1 -0
- package/scripts/lane/lane-withdraw-cli.ts +71 -0
- package/scripts/lane/lane-withdraw.ts +182 -0
- package/scripts/lint/format.ts +4 -4
- package/scripts/lint/lint-parallel.ts +10 -3
- package/scripts/lint/lint-related-scope.ts +4 -1
- package/scripts/managed-marker/managed-marker-logic.ts +1 -2
- package/scripts/notify/telegram-test.ts +10 -5
- package/scripts/overrides/lockfile-overrides.ts +1 -3
- package/scripts/propagate/propagate.ts +1 -2
- package/scripts/release/release-progress.ts +3 -2
- package/scripts/report/report-format-reference.ts +2 -0
- package/scripts/report/report-lint.ts +7 -4
- package/scripts/review/acceptance-map-check.ts +61 -0
- package/scripts/review/acceptance-map.ts +205 -0
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-record-cli.ts +6 -2
- package/scripts/rules/agent-model.ts +59 -0
- package/scripts/rules/delivered-rules.ts +11 -2
- package/scripts/rules/destructive-command.ts +6 -9
- package/scripts/rules/direct-filing.ts +3 -7
- package/scripts/rules/early-heartbeat.ts +7 -18
- package/scripts/rules/file-body.ts +6 -13
- package/scripts/rules/filing-cap.ts +4 -6
- package/scripts/rules/firing-point.ts +6 -5
- package/scripts/rules/git-force.ts +5 -9
- package/scripts/rules/guard-remedy.ts +117 -0
- package/scripts/rules/implementation-cut.ts +37 -35
- package/scripts/rules/index-guard.ts +4 -7
- package/scripts/rules/issue-comments.ts +67 -16
- package/scripts/rules/josh-git-bare.ts +3 -6
- package/scripts/rules/lane-background.ts +10 -21
- package/scripts/rules/lane-carry-conflict.ts +7 -17
- package/scripts/rules/lane-interactive-ask.ts +6 -11
- package/scripts/rules/lane-park.ts +5 -13
- package/scripts/rules/lane-split-park.ts +2 -7
- package/scripts/rules/lane-switch-main.ts +9 -12
- package/scripts/rules/last-prompt.ts +50 -5
- package/scripts/rules/oracle-consulted.ts +4 -5
- package/scripts/rules/oracle-list-cli.ts +7 -0
- package/scripts/rules/piped-verification.ts +84 -32
- package/scripts/rules/poll-loop.ts +7 -17
- package/scripts/rules/pre-gate-cut.ts +7 -21
- package/scripts/rules/prior-comment-read.ts +7 -5
- package/scripts/rules/protected-files.ts +5 -9
- package/scripts/rules/raw-field-body.ts +3 -7
- package/scripts/rules/rule-body-guard.ts +4 -11
- package/scripts/rules/rule-list.ts +4 -1
- package/scripts/rules/rule-registry-rows.ts +10 -0
- package/scripts/rules/rule-registry.ts +7 -4
- package/scripts/rules/rule-rewrite.ts +114 -0
- package/scripts/rules/rule-value.ts +7 -1
- package/scripts/rules/run-tail-rule.ts +6 -22
- package/scripts/rules/shell-body-trigger.ts +4 -6
- package/scripts/rules/shell-segments.ts +22 -0
- package/scripts/rules/stop-decision.ts +77 -0
- package/scripts/rules/stop-rules.ts +164 -77
- package/scripts/rules/tail-commands.ts +15 -1
- package/scripts/rules/test-declared-commit.ts +4 -10
- package/scripts/rules/third-party-write.ts +5 -10
- package/scripts/rules/worktree-guard.ts +5 -12
- package/scripts/run/board/run-board-cli.ts +2 -0
- package/scripts/run/board/run-board-closed.ts +8 -2
- package/scripts/run/board/run-board-github.ts +19 -3
- package/scripts/run/board/run-board-header.ts +4 -1
- package/scripts/run/board/run-board-idle.ts +31 -0
- package/scripts/run/board/run-board-labels.ts +2 -0
- package/scripts/run/board/run-board-layout.ts +1 -1
- package/scripts/run/board/run-board-read.ts +25 -12
- package/scripts/run/board/run-board-reason.ts +17 -4
- package/scripts/run/board/run-board-render-legend.ts +1 -0
- package/scripts/run/board/run-board-render-slot.ts +1 -1
- package/scripts/run/board/run-board-render-waits.ts +41 -0
- package/scripts/run/board/run-board-render.ts +6 -1
- package/scripts/run/board/run-board-row-yield.ts +1 -0
- package/scripts/run/board/run-board-state.ts +9 -0
- package/scripts/run/board/run-board-status.ts +89 -23
- package/scripts/run/board/run-board-thanks.ts +7 -2
- package/scripts/run/board/run-board-tick.ts +37 -8
- package/scripts/run/carry/run-carry-args.ts +1 -3
- package/scripts/run/carry/run-carry-conversation.ts +7 -2
- package/scripts/run/cut/run-cut-cli.ts +18 -22
- package/scripts/run/cut/run-cut-decline.ts +72 -0
- package/scripts/run/cut/run-cut-ready.ts +74 -0
- package/scripts/run/cut/run-cut-report.ts +29 -0
- package/scripts/run/cut/run-cut.ts +10 -1
- package/scripts/run/detached-launch.ts +23 -4
- package/scripts/run/entry/run-entry-cli.ts +8 -1
- package/scripts/run/event/run-event-render.ts +1 -0
- package/scripts/run/event/run-event-scope.ts +19 -1
- package/scripts/run/event/run-event-stream-emit.ts +13 -6
- package/scripts/run/event/run-event-stream.ts +4 -0
- package/scripts/run/hold/run-hold-cli.ts +14 -3
- package/scripts/run/hold/run-hold-session.ts +46 -0
- package/scripts/run/hold/run-hold.ts +13 -4
- package/scripts/run/launch-priority.ts +32 -0
- package/scripts/run/merge/run-merge-cli.ts +16 -42
- package/scripts/run/merge/run-merge-ship.ts +59 -1
- package/scripts/run/merge/run-merge-signals.ts +99 -0
- package/scripts/run/merge/run-merge-steps.ts +11 -1
- package/scripts/run/merge/run-merge.ts +22 -9
- package/scripts/run/run-ending.ts +31 -3
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +5 -10
- package/scripts/run/run-poll-hook.ts +157 -0
- package/scripts/run/run-preflight.ts +1 -2
- package/scripts/run/run-step-cli.ts +22 -15
- package/scripts/run/run-step-section.ts +85 -0
- package/scripts/run/run-stop-notify.ts +1 -2
- package/scripts/run/run-watcher-guard.ts +6 -3
- package/scripts/run/ship/run-ship-cli.ts +5 -12
- package/scripts/run/ship/run-ship-detach.ts +9 -0
- package/scripts/run/ship/run-ship-next.ts +10 -2
- package/scripts/run/ship/run-ship-overlap.ts +195 -0
- package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
- package/scripts/run/ship/run-ship-preflight.ts +28 -2
- package/scripts/run/ship/run-ship-return.ts +31 -7
- package/scripts/run/ship/run-ship-review-steps.ts +25 -14
- package/scripts/run/ship/run-ship-review.ts +15 -2
- package/scripts/run/ship/run-ship-steps.ts +1 -1
- package/scripts/run/ship/run-ship-stop-context.ts +206 -0
- package/scripts/run/ship/run-ship-sync.ts +14 -6
- package/scripts/run/tidy/run-tidy-cli.ts +3 -3
- package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
- package/scripts/safe-chain/project-config.ts +7 -2
- package/scripts/sync/sync-hook-safety.ts +8 -2
- package/scripts/test/test-e2e-guard.ts +30 -5
- package/scripts/test/test-unit-guard.ts +16 -1
- package/scripts/test/unit-worker-share.ts +26 -3
- package/scripts/time-runtime/time-batch-guard.ts +3 -3
- package/scripts/time-runtime/time-hook-transcript.ts +3 -2
- package/scripts/time-runtime/time-shell.ts +39 -4
- package/scripts/time-runtime/time-spans.ts +1 -0
- package/scripts/time-runtime/time-tool-call.ts +7 -0
- package/scripts/version/effective-upstream.ts +3 -4
- package/scripts/version/kit-version-config.ts +8 -2
- package/scripts/version/latest-corepack.ts +1 -2
- package/scripts/version/release-hold.ts +11 -4
- package/scripts/version/version-commands.ts +12 -4
- package/templates/gitignore +6 -2
- package/dist/hooks/chunk-D5QBTOCZ.js +0 -68
- package/dist/hooks/chunk-UT7YC3N2.js +0 -56
|
@@ -2,10 +2,13 @@ import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
|
2
2
|
import { issue_cite } from '#scripts/issue/issue-cite'
|
|
3
3
|
import { josh_command, type JoshResult } from '#scripts/josh/josh-run'
|
|
4
4
|
import { lane_capacity } from '#scripts/lane/lane-capacity'
|
|
5
|
-
import { lane_registry } from '#scripts/lane/lane-registry'
|
|
5
|
+
import { lane_registry, type LaneInfo } from '#scripts/lane/lane-registry'
|
|
6
|
+
import { lane_spent } from '#scripts/lane/lane-spent'
|
|
6
7
|
import type { RunCarry } from '#scripts/run/carry/run-carry'
|
|
8
|
+
import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
|
|
7
9
|
import { run_headless } from '#scripts/run/run-headless'
|
|
8
10
|
import { run_invocation } from '#scripts/run/run-invocation'
|
|
11
|
+
import { backlog_occupied, type OccupiedRead } from './backlog-occupied'
|
|
9
12
|
import { backlog_stalled } from './backlog-stalled'
|
|
10
13
|
|
|
11
14
|
// The ready line reports runnable work to the driving backlogrun parent on a watcher wake.
|
|
@@ -18,18 +21,60 @@ interface ReadyReading {
|
|
|
18
21
|
free_lanes: number
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
// How many open lanes hold a place under the limit, and how many hold a port seat.
|
|
25
|
+
interface LaneUse {
|
|
26
|
+
occupied: number
|
|
27
|
+
seated: number
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
interface ReadyPorts {
|
|
22
31
|
free_lane_count: () => Promise<number>
|
|
23
32
|
ready_issues: () => Promise<ReadonlyArray<string>>
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
//
|
|
27
|
-
async function
|
|
28
|
-
|
|
35
|
+
// What decides which open lanes hold a place, read from this checkout's live run.
|
|
36
|
+
async function occupied_read(): Promise<OccupiedRead> {
|
|
37
|
+
const carry = await run_headless.current_carry()
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
invocation: carry?.invocation,
|
|
41
|
+
events: carry === undefined ? [] : await run_event_stream_emit.scoped_current_events(),
|
|
42
|
+
idle: lane_spent.idle_lanes,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The lanes that hold a place under the limit (`backlog-occupied.ts`).
|
|
47
|
+
async function occupied_lanes(lanes?: ReadonlyArray<LaneInfo>): Promise<Array<LaneInfo>> {
|
|
48
|
+
const open = lanes ?? (await lane_registry.list_lanes())
|
|
49
|
+
|
|
50
|
+
return backlog_occupied.occupied_lanes(open, await occupied_read())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The open lanes as the free count reads them: those that hold a place under the limit, and every
|
|
54
|
+
// open one, since an uncounted lane still holds its port seat.
|
|
55
|
+
async function lane_use(lanes?: ReadonlyArray<LaneInfo>): Promise<LaneUse> {
|
|
56
|
+
const open = lanes ?? (await lane_registry.list_lanes())
|
|
57
|
+
const occupied = await occupied_lanes(open)
|
|
58
|
+
|
|
59
|
+
return { occupied: occupied.length, seated: open.filter((lane) => !lane.is_stranded).length }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function free_of(limit: number, use: LaneUse): number {
|
|
63
|
+
return lane_capacity.free_lanes(limit, use.occupied, use.seated)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Free lanes beside `lanes`, `undefined` where the limit could not be read: the count `lane:limit`
|
|
67
|
+
// prints, and the one `run:board` draws, so the two never answer apart.
|
|
68
|
+
async function free_lanes_beside(lanes: ReadonlyArray<LaneInfo>): Promise<number | undefined> {
|
|
69
|
+
const limit = await lane_capacity.lane_limit()
|
|
70
|
+
|
|
71
|
+
if (limit.kind !== 'limit') return undefined
|
|
72
|
+
|
|
73
|
+
return free_of(limit.limit, await lane_use(lanes))
|
|
29
74
|
}
|
|
30
75
|
|
|
31
76
|
async function free_under(limit: number): Promise<number> {
|
|
32
|
-
return
|
|
77
|
+
return free_of(limit, await lane_use())
|
|
33
78
|
}
|
|
34
79
|
|
|
35
80
|
// Free lanes right now: the limit less the live lanes. An unreadable limit reads as no free lane — the
|
|
@@ -38,15 +83,12 @@ async function free_under(limit: number): Promise<number> {
|
|
|
38
83
|
//
|
|
39
84
|
// **Occupancy is the local worktree count, not the dispatch budget's busy read.** `epic-lane-offer`
|
|
40
85
|
// measures occupancy with `epic_busy.occupied_lanes`, a network read of in-progress issue labels; this
|
|
41
|
-
// counts
|
|
42
|
-
// (`backlog:next`) is gated behind a free lane, so a network
|
|
43
|
-
// two can diverge, but only toward over-counting live lanes →
|
|
86
|
+
// counts the worktrees that hold a place instead (`backlog-occupied.ts`), deliberately — it runs on the
|
|
87
|
+
// cheap-first path where the costly read (`backlog:next`) is gated behind a free lane, so a network
|
|
88
|
+
// read here would defeat that design. The two can diverge, but only toward over-counting live lanes →
|
|
89
|
+
// fewer free lanes, the safe direction.
|
|
44
90
|
async function free_lane_count(): Promise<number> {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (limit.kind !== 'limit') return NO_FREE
|
|
48
|
-
|
|
49
|
-
return await free_under(limit.limit)
|
|
91
|
+
return (await free_lanes_beside(await lane_registry.list_lanes())) ?? NO_FREE
|
|
50
92
|
}
|
|
51
93
|
|
|
52
94
|
// **The drive's count throws on an unreadable limit**. The drive launches no
|
|
@@ -165,7 +207,9 @@ const backlog_ready = {
|
|
|
165
207
|
read_backlog_next,
|
|
166
208
|
print_offer_hint,
|
|
167
209
|
free_lane_count,
|
|
168
|
-
|
|
210
|
+
free_lanes_beside,
|
|
211
|
+
lane_use,
|
|
212
|
+
occupied_lanes,
|
|
169
213
|
drive_free_lane_count,
|
|
170
214
|
drains_pool,
|
|
171
215
|
print_ready_line,
|
|
@@ -175,4 +219,4 @@ const backlog_ready = {
|
|
|
175
219
|
}
|
|
176
220
|
|
|
177
221
|
export { backlog_ready }
|
|
178
|
-
export type { ReadyPorts, ReadyReading }
|
|
222
|
+
export type { LaneUse, ReadyPorts, ReadyReading }
|
|
@@ -53,8 +53,9 @@ const INDEX_ASSERTION_NAME = 'no-direct-git-index-mutation'
|
|
|
53
53
|
// a tool result, or a call to any tool but `Bash`. Read through `json_value.is_record` so a malformed
|
|
54
54
|
// input is treated as absent rather than throwing mid-scan.
|
|
55
55
|
function command_of(block: Block): string | undefined {
|
|
56
|
-
if (block.type !== TOOL_USE || block.name !== BASH_TOOL)
|
|
57
|
-
|
|
56
|
+
if (block.type !== TOOL_USE || block.name !== BASH_TOOL || !json_value.is_record(block.input)) {
|
|
57
|
+
return undefined
|
|
58
|
+
}
|
|
58
59
|
|
|
59
60
|
const command = block.input[COMMAND_KEY]
|
|
60
61
|
|
|
@@ -56,8 +56,10 @@ function measurement_from(
|
|
|
56
56
|
return parsed.success ? [parsed.data.payload.info] : []
|
|
57
57
|
})
|
|
58
58
|
const session = metadata.find((entry) => entry.payload.id === thread_id)
|
|
59
|
-
|
|
60
|
-
if (!same_project(session.payload.cwd, target))
|
|
59
|
+
|
|
60
|
+
if (session === undefined || usages.length === 0 || !same_project(session.payload.cwd, target)) {
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
61
63
|
|
|
62
64
|
return { billed_input_per_request: billed_per_request(usages) }
|
|
63
65
|
}
|
|
@@ -56,8 +56,7 @@ interface RunContext {
|
|
|
56
56
|
// `--over=` does not arrive as "hand off after any request" and stop an unattended run after its
|
|
57
57
|
// first child.
|
|
58
58
|
function to_threshold(raw: string | undefined): number | undefined {
|
|
59
|
-
if (raw === undefined) return undefined
|
|
60
|
-
if (raw.trim() === '') return undefined
|
|
59
|
+
if (raw === undefined || raw.trim() === '') return undefined
|
|
61
60
|
|
|
62
61
|
const parsed = Number(raw)
|
|
63
62
|
|
|
@@ -176,8 +176,8 @@ function is_candidate(call: GuardedCall): boolean {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// A refusable call is refused only where the mode is `refuse`. A lane child is `notice`, so it reaches
|
|
179
|
-
// here and is withheld the refusal — the notice branch carries the guidance instead,
|
|
180
|
-
//
|
|
179
|
+
// here and is withheld the refusal — the notice branch carries the guidance instead, for the reason its
|
|
180
|
+
// `because` in `lane-guard-policy.ts` records.
|
|
181
181
|
function should_block_here(tail: string, call: GuardedCall, refused_at_ms: number): boolean {
|
|
182
182
|
if (duplicate_mode() !== 'refuse') return false
|
|
183
183
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { text } from 'node:stream/consumers'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { agent_session_role } from '#scripts/agent/agent-session-role'
|
|
4
5
|
import { hook_decision, type GuardRun } from '#scripts/josh/hook-decision'
|
|
5
|
-
import { lane_guard_policy
|
|
6
|
+
import { lane_guard_policy } from '#scripts/lane/lane-guard-policy'
|
|
6
7
|
import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
|
|
7
8
|
import { investigation_reads } from './investigation-reads'
|
|
8
9
|
|
|
@@ -21,91 +22,42 @@ import { investigation_reads } from './investigation-reads'
|
|
|
21
22
|
// file — debugging the guard itself, or a session whose reading really is all edit targets.
|
|
22
23
|
const SWITCH_ENV_KEY = 'JOSH_INVESTIGATION_GUARD'
|
|
23
24
|
|
|
24
|
-
// This guard's refusal record, and the notice's own record — distinct so a lane-child notice never
|
|
25
|
-
// spends the refusal's stamp, the same split the batching guard keeps.
|
|
26
25
|
const STAMP_PREFIX = 'josh-investigation-guard-'
|
|
27
|
-
const NOTICE_STAMP_PREFIX = 'josh-investigation-guard-notice-'
|
|
28
26
|
|
|
29
|
-
// **How the guard behaves for the session in hand, decided from the one-place enumeration**.
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
function investigation_mode(): LaneGuardMode {
|
|
37
|
-
return lane_guard_policy.mode_here('investigation')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// A refusable call is refused only where the mode is `refuse` — the main line. A lane child is
|
|
41
|
-
// `notice`, so it reaches the notice branch and is withheld the refusal that would end its headless
|
|
42
|
-
// turn; an `off` some future enumeration sets stays silent here too.
|
|
27
|
+
// **How the guard behaves for the session in hand, decided from the one-place enumeration**. It is
|
|
28
|
+
// `refuse` in a dispatched lane child too (joshuafolkken/kit#3927): a hook denial returns to the model
|
|
29
|
+
// as guidance, and the notice it replaced was measured not to move the reading. It wraps
|
|
30
|
+
// `investigation_reads.should_block` here rather than inside it so the pure counting rule stays free of
|
|
31
|
+
// process state and its own suite keeps testing it directly. A reviewer session stands down: reading
|
|
32
|
+
// the diff's files is its whole task, and it carries the lane's marker, so it would otherwise be refused
|
|
33
|
+
// like the child it reviews — the stand-down `implementation-cut.ts` takes for the same session.
|
|
43
34
|
function should_block(
|
|
44
35
|
tail: string,
|
|
45
36
|
call: GuardedCall,
|
|
46
37
|
refused_at_ms: number,
|
|
47
38
|
run?: GuardRun,
|
|
48
39
|
): boolean {
|
|
49
|
-
if (
|
|
40
|
+
if (
|
|
41
|
+
agent_session_role.is_reviewer() ||
|
|
42
|
+
lane_guard_policy.mode_here('investigation') !== 'refuse'
|
|
43
|
+
) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
50
46
|
|
|
51
47
|
return investigation_reads.should_block(tail, call, refused_at_ms, run)
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
// The lane-child notice: the same threshold, delivered without a `permissionDecision`. It
|
|
55
|
-
// reads the notify stamp's own record, so a notice never spends the refusal's stamp and re-arms on the
|
|
56
|
-
// next accumulation exactly as the refusal does.
|
|
57
|
-
function should_notify(
|
|
58
|
-
tail: string,
|
|
59
|
-
call: GuardedCall,
|
|
60
|
-
notified_at_ms: number,
|
|
61
|
-
run?: GuardRun,
|
|
62
|
-
): boolean {
|
|
63
|
-
if (investigation_mode() !== 'notice') return false
|
|
64
|
-
|
|
65
|
-
return investigation_reads.should_block(tail, call, notified_at_ms, run)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// **The lane-child counterpart of REASON, delivered as a notice rather than a refusal**.
|
|
69
|
-
// A dispatched lane child ends its turn on a denial, so
|
|
70
|
-
// the same guidance is carried without a `permissionDecision`: the read proceeds and the child is nudged
|
|
71
|
-
// to delegate rather than killed. It carries no ⛔, so a person watching reads it as advice.
|
|
72
|
-
const NOTICE = `💡 investigation: files read and not edited, or search turns, since the last delegated unit reached the threshold, so the reading from here should go to a unit of its own. This is a notice, not a refusal — the read proceeds, because a dispatched lane child ends its turn on a denial. Ask \`pnpm josh delegate investigation\`, then dispatch the \`${investigation_reads.INVESTIGATOR_AGENT}\` agent (\`kit:${investigation_reads.INVESTIGATOR_AGENT}\` in a consumer) with a brief of what the main line has already concluded and what is left to find out; it returns the conclusion plus its \`file:line\` citations, never the file text. Keep a read in the main line only where this run will edit that file. This recurs on the next accumulation.`
|
|
73
|
-
|
|
74
|
-
// The notice as it reaches the model: the base guidance with the concrete unedited reads named, so a
|
|
75
|
-
// lane child is shown *which* reads to send to a unit rather than only told to delegate. An empty
|
|
76
|
-
// set names nothing and the notice falls back to its guidance alone. The call is part of the notify
|
|
77
|
-
// contract but this guard's wording depends on the tail alone.
|
|
78
|
-
function notice_text(_call: GuardedCall, tail: string): string {
|
|
79
|
-
const pending = investigation_reads
|
|
80
|
-
.tally_of(tail)
|
|
81
|
-
.pending.map((absolute) => investigation_reads.repository_relative(absolute))
|
|
82
|
-
|
|
83
|
-
return pending.length === 0
|
|
84
|
-
? NOTICE
|
|
85
|
-
: `${NOTICE} Read and not edited so far: ${pending.join(' ')}.`
|
|
86
|
-
}
|
|
87
|
-
|
|
88
50
|
const GUARD = hook_decision.create_transcript_guard({
|
|
89
51
|
prefix: STAMP_PREFIX,
|
|
90
52
|
switch_key: SWITCH_ENV_KEY,
|
|
91
53
|
is_candidate: investigation_reads.is_refusable_call,
|
|
92
54
|
should_block,
|
|
93
55
|
reason: investigation_reads.REASON,
|
|
94
|
-
// The lane-child notice fires where the refusal is withheld, on a record of its own so it
|
|
95
|
-
// never spends the refusal's stamp.
|
|
96
|
-
notify: {
|
|
97
|
-
prefix: NOTICE_STAMP_PREFIX,
|
|
98
|
-
should_notify,
|
|
99
|
-
text: notice_text,
|
|
100
|
-
},
|
|
101
56
|
})
|
|
102
57
|
|
|
103
58
|
const investigation_refusal = GUARD.refusal
|
|
104
59
|
const investigation_outcome = GUARD.outcome
|
|
105
60
|
const { is_enabled, refusal_path } = GUARD
|
|
106
|
-
// The notice's stamp path, exposed the way `refusal_path` is so a test can clean it apart from the
|
|
107
|
-
// refusal's record.
|
|
108
|
-
const investigation_notice_path = hook_decision.create_refusal_stamp(NOTICE_STAMP_PREFIX).path
|
|
109
61
|
const { deny_envelope, load_environment_file, DISABLED_VALUES } = hook_decision
|
|
110
62
|
|
|
111
63
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
@@ -115,13 +67,11 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
|
115
67
|
|
|
116
68
|
export {
|
|
117
69
|
deny_envelope,
|
|
118
|
-
investigation_notice_path,
|
|
119
70
|
investigation_outcome,
|
|
120
71
|
investigation_refusal,
|
|
121
72
|
is_enabled,
|
|
122
73
|
load_environment_file,
|
|
123
74
|
refusal_path,
|
|
124
75
|
DISABLED_VALUES,
|
|
125
|
-
NOTICE,
|
|
126
76
|
SWITCH_ENV_KEY,
|
|
127
77
|
}
|
|
@@ -42,7 +42,7 @@ const SEARCH_LABELS: ReadonlySet<string> = new Set(
|
|
|
42
42
|
const INVESTIGATOR_AGENT = 'investigator'
|
|
43
43
|
|
|
44
44
|
// The deny reason is the only text that reaches the model, so it names the count, command and return shape.
|
|
45
|
-
const REASON = `⛔ investigation: ${String(delegation_policy.INVESTIGATION_FILE_THRESHOLD)} files read and not edited, or ${String(delegation_policy.INVESTIGATION_SEARCH_TURN_THRESHOLD)} search turns, since the last delegated unit
|
|
45
|
+
const REASON = `⛔ investigation: ${String(delegation_policy.INVESTIGATION_FILE_THRESHOLD)} files read and not edited, or ${String(delegation_policy.INVESTIGATION_SEARCH_TURN_THRESHOLD)} search turns, since the last delegated unit. Run \`pnpm josh delegate investigation\`, then dispatch the \`${INVESTIGATOR_AGENT}\` agent (\`kit:${INVESTIGATOR_AGENT}\` in a consumer); it returns \`file:line\` citations, never the file text. Keep main-line reads to files you will edit; every read-only search turn counts. \`.claude/skills/workflow-commands/delegation.md\``
|
|
46
46
|
|
|
47
47
|
interface ReadTally {
|
|
48
48
|
// The files read and not since edited, resolved.
|
|
@@ -362,7 +362,6 @@ const investigation_reads = {
|
|
|
362
362
|
is_session_artifact,
|
|
363
363
|
is_subject_file,
|
|
364
364
|
projected_count,
|
|
365
|
-
repository_relative,
|
|
366
365
|
resolved,
|
|
367
366
|
should_block,
|
|
368
367
|
tally_of,
|
|
@@ -138,8 +138,10 @@ function resolve_current_owner(repository_path: string): string | undefined {
|
|
|
138
138
|
// all — the very failure this function exists to close, moved somewhere else.
|
|
139
139
|
function main_worktree(repository_path: string): string {
|
|
140
140
|
const git_directory = resolve_git_directory(repository_path)
|
|
141
|
-
|
|
142
|
-
if (path.basename(git_directory) !== GIT_DIRECTORY)
|
|
141
|
+
|
|
142
|
+
if (git_directory === undefined || path.basename(git_directory) !== GIT_DIRECTORY) {
|
|
143
|
+
return repository_path
|
|
144
|
+
}
|
|
143
145
|
|
|
144
146
|
return path.dirname(git_directory)
|
|
145
147
|
}
|
|
@@ -68,8 +68,7 @@ function parse_scp_like(url: string): RepoIdentity | undefined {
|
|
|
68
68
|
const user_end = url.indexOf(USER_SEPARATOR)
|
|
69
69
|
const authority = user_end === NOT_FOUND ? url : url.slice(user_end + 1)
|
|
70
70
|
const host_end = authority.indexOf(HOST_SEPARATOR)
|
|
71
|
-
if (host_end === NOT_FOUND) return undefined
|
|
72
|
-
if (!is_github_ssh_host(authority.slice(0, host_end))) return undefined
|
|
71
|
+
if (host_end === NOT_FOUND || !is_github_ssh_host(authority.slice(0, host_end))) return undefined
|
|
73
72
|
|
|
74
73
|
return to_identity(authority.slice(host_end + 1))
|
|
75
74
|
}
|
|
@@ -115,8 +114,7 @@ function origin_section_lines(lines: ReadonlyArray<string>): Array<string> {
|
|
|
115
114
|
// the first separator rather than matched, for the reason `parse_scp_like` is split by hand.
|
|
116
115
|
function url_value(line: string): string | undefined {
|
|
117
116
|
const separator = line.indexOf('=')
|
|
118
|
-
if (separator === NOT_FOUND) return undefined
|
|
119
|
-
if (line.slice(0, separator).trim() !== URL_KEY) return undefined
|
|
117
|
+
if (separator === NOT_FOUND || line.slice(0, separator).trim() !== URL_KEY) return undefined
|
|
120
118
|
|
|
121
119
|
return line.slice(separator + 1).trim()
|
|
122
120
|
}
|
|
@@ -69,9 +69,12 @@ function decide_reclaim(
|
|
|
69
69
|
global_josh: string | undefined,
|
|
70
70
|
shim_content: string,
|
|
71
71
|
): ReclaimDecision {
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
if (path_josh === undefined
|
|
72
|
+
// is_shadowed already implies path_josh is defined; the undefined check narrows the type for the
|
|
73
|
+
// return.
|
|
74
|
+
if (path_josh === undefined || !is_shadowed(path_josh, global_josh)) {
|
|
75
|
+
return { action: 'none', reason: NO_SHADOW_REASON }
|
|
76
|
+
}
|
|
77
|
+
|
|
75
78
|
if (!is_kit_shim(shim_content)) return { action: 'none', reason: NOT_KIT_SHIM_REASON }
|
|
76
79
|
|
|
77
80
|
return { action: 'remove', target: path_josh, reason: RECLAIM_REASON }
|
|
@@ -57,52 +57,53 @@ function block_ceiling(size: number): number {
|
|
|
57
57
|
// a stale entry (a file that no longer exists or that an entry now reads) and on an un-budgeted
|
|
58
58
|
// unreached file, so the definition cannot rot as documents move on or off the execution path.
|
|
59
59
|
const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
60
|
-
{ path: '.claude/skills/dependency-update/SKILL.md', bytes:
|
|
61
|
-
{ path: '.claude/skills/epic-commands/SKILL.md', bytes:
|
|
62
|
-
{ path: '.claude/skills/epic-commands/epic-bundle.md', bytes:
|
|
63
|
-
{ path: '.claude/skills/epic-commands/execution-waves.md', bytes:
|
|
64
|
-
{ path: '.claude/skills/verify-ui/SKILL.md', bytes:
|
|
60
|
+
{ path: '.claude/skills/dependency-update/SKILL.md', bytes: 4096 },
|
|
61
|
+
{ path: '.claude/skills/epic-commands/SKILL.md', bytes: 12_288 },
|
|
62
|
+
{ path: '.claude/skills/epic-commands/epic-bundle.md', bytes: 4096 },
|
|
63
|
+
{ path: '.claude/skills/epic-commands/execution-waves.md', bytes: 4096 },
|
|
64
|
+
{ path: '.claude/skills/verify-ui/SKILL.md', bytes: 4096 },
|
|
65
65
|
{ path: '.claude/skills/workflow-commands/backlogrun-handoff-report.md', bytes: 4096 },
|
|
66
66
|
{ path: '.claude/skills/workflow-commands/fullrun-steps.md', bytes: 8192 },
|
|
67
67
|
{ path: '.claude/skills/workflow-commands/into-target.md', bytes: 4096 },
|
|
68
68
|
{ path: '.claude/skills/workflow-commands/issue-comments.md', bytes: 8192 },
|
|
69
69
|
{ path: '.claude/skills/workflow-commands/issue-fold-existing.md', bytes: 4096 },
|
|
70
70
|
{ path: '.claude/skills/workflow-commands/needs-human-review.md', bytes: 4096 },
|
|
71
|
-
{ path: '.claude/skills/workflow-commands/observation-filing.md', bytes:
|
|
72
|
-
{ path: '.claude/skills/workflow-commands/observation-ledger.md', bytes:
|
|
71
|
+
{ path: '.claude/skills/workflow-commands/observation-filing.md', bytes: 8192 },
|
|
72
|
+
{ path: '.claude/skills/workflow-commands/observation-ledger.md', bytes: 8192 },
|
|
73
73
|
{ path: '.claude/skills/workflow-commands/prerequisite.md', bytes: 8192 },
|
|
74
74
|
{ path: '.claude/skills/workflow-commands/target-repository.md', bytes: 4096 },
|
|
75
75
|
{ path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
|
|
76
|
-
{ path: 'docs/josh-commands-automation.md', bytes:
|
|
77
|
-
{ path: 'docs/josh-commands-run.md', bytes:
|
|
78
|
-
{ path: 'docs/josh-commands-backlog.md', bytes:
|
|
79
|
-
{ path: 'docs/josh-commands.md', bytes:
|
|
76
|
+
{ path: 'docs/josh-commands-automation.md', bytes: 49_152 },
|
|
77
|
+
{ path: 'docs/josh-commands-run.md', bytes: 65_536 },
|
|
78
|
+
{ path: 'docs/josh-commands-backlog.md', bytes: 53_248 },
|
|
79
|
+
{ path: 'docs/josh-commands.md', bytes: 49_152 },
|
|
80
80
|
{ path: 'docs/labels-and-run-states.md', bytes: 8192 },
|
|
81
|
-
{ path: 'prompts/coding-standards.md', bytes:
|
|
82
|
-
{ path: 'prompts/collaboration-workflow.md', bytes:
|
|
83
|
-
{ path: 'prompts/collaboration-workflow/file-edits.md', bytes:
|
|
84
|
-
{ path: 'prompts/collaboration-workflow/gh-rest.md', bytes:
|
|
81
|
+
{ path: 'prompts/coding-standards.md', bytes: 4096 },
|
|
82
|
+
{ path: 'prompts/collaboration-workflow.md', bytes: 4096 },
|
|
83
|
+
{ path: 'prompts/collaboration-workflow/file-edits.md', bytes: 4096 },
|
|
84
|
+
{ path: 'prompts/collaboration-workflow/gh-rest.md', bytes: 4096 },
|
|
85
85
|
{ path: 'prompts/collaboration-workflow/glossary.md', bytes: 4096 },
|
|
86
86
|
{ path: 'prompts/collaboration-workflow/issue-citation.md', bytes: 4096 },
|
|
87
|
-
{ path: 'prompts/collaboration-workflow/issue-template.md', bytes:
|
|
88
|
-
{ path: 'prompts/collaboration-workflow/
|
|
87
|
+
{ path: 'prompts/collaboration-workflow/issue-template.md', bytes: 8192 },
|
|
88
|
+
{ path: 'prompts/collaboration-workflow/lane-instructions.md', bytes: 4096 },
|
|
89
|
+
{ path: 'prompts/collaboration-workflow/operating-rules.md', bytes: 8192 },
|
|
89
90
|
{ path: 'prompts/collaboration-workflow/output-bounds.md', bytes: 4096 },
|
|
90
91
|
{ path: 'prompts/collaboration-workflow/overview.md', bytes: 4096 },
|
|
91
|
-
{ path: 'prompts/collaboration-workflow/plan-comment.md', bytes:
|
|
92
|
-
{ path: 'prompts/collaboration-workflow/principles.md', bytes:
|
|
92
|
+
{ path: 'prompts/collaboration-workflow/plan-comment.md', bytes: 4096 },
|
|
93
|
+
{ path: 'prompts/collaboration-workflow/principles.md', bytes: 12_288 },
|
|
93
94
|
{ path: 'prompts/collaboration-workflow/proposal-request.md', bytes: 4096 },
|
|
94
|
-
{ path: 'prompts/collaboration-workflow/report-format.md', bytes:
|
|
95
|
+
{ path: 'prompts/collaboration-workflow/report-format.md', bytes: 12_288 },
|
|
95
96
|
{ path: 'prompts/collaboration-workflow/residency.md', bytes: 8192 },
|
|
96
97
|
{ path: 'prompts/collaboration-workflow/rule-delivery.md', bytes: 4096 },
|
|
97
|
-
{ path: 'prompts/collaboration-workflow/shell-body.md', bytes:
|
|
98
|
-
{ path: 'prompts/collaboration-workflow/turn-batching.md', bytes:
|
|
99
|
-
{ path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes:
|
|
100
|
-
{ path: 'prompts/collaboration-workflow/wip-cap.md', bytes:
|
|
98
|
+
{ path: 'prompts/collaboration-workflow/shell-body.md', bytes: 4096 },
|
|
99
|
+
{ path: 'prompts/collaboration-workflow/turn-batching.md', bytes: 4096 },
|
|
100
|
+
{ path: 'prompts/collaboration-workflow/upstream-interrupt.md', bytes: 8192 },
|
|
101
|
+
{ path: 'prompts/collaboration-workflow/wip-cap.md', bytes: 8192 },
|
|
101
102
|
{ path: 'prompts/refactoring.md', bytes: 8192 },
|
|
102
|
-
{ path: 'prompts/review-rubric.md', bytes:
|
|
103
|
-
{ path: 'prompts/review.md', bytes:
|
|
103
|
+
{ path: 'prompts/review-rubric.md', bytes: 12_288 },
|
|
104
|
+
{ path: 'prompts/review.md', bytes: 12_288 },
|
|
104
105
|
{ path: 'prompts/sonar-hotspot-handling.md', bytes: 8192 },
|
|
105
|
-
{ path: 'prompts/testing-guide.md', bytes:
|
|
106
|
+
{ path: 'prompts/testing-guide.md', bytes: 12_288 },
|
|
106
107
|
]
|
|
107
108
|
|
|
108
109
|
// The recorded ceiling for one path, or undefined when it carries no budget entry.
|
|
@@ -26,8 +26,7 @@ function ceiling_message(
|
|
|
26
26
|
current_bytes: number,
|
|
27
27
|
recorded_bytes: number | undefined,
|
|
28
28
|
): string | undefined {
|
|
29
|
-
if (recorded_bytes === undefined) return undefined
|
|
30
|
-
if (current_bytes <= recorded_bytes) return undefined
|
|
29
|
+
if (recorded_bytes === undefined || current_bytes <= recorded_bytes) return undefined
|
|
31
30
|
|
|
32
31
|
return document_byte_budget.over_budget_message(relative_path, current_bytes, recorded_bytes)
|
|
33
32
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { find_package_directory } from '#scripts/josh/package-directory'
|
|
5
6
|
import { capped_output } from './capped-print'
|
|
6
7
|
import { document_section } from './document-section'
|
|
7
8
|
import { entry_read_set } from './entry-read-set'
|
|
@@ -35,14 +36,25 @@ const USAGE = 'Usage: josh doc:section <file.md> "<heading>"'
|
|
|
35
36
|
// directory, a bare name that happened to exist beside the caller would beat the document under
|
|
36
37
|
// `root` and print a section of the wrong file — silently, which is the failure this command exists
|
|
37
38
|
// to refuse.
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
//
|
|
40
|
+
// **The kit package is the fallback.** A consumer keeps no `prompts/` of its own — the package ships
|
|
41
|
+
// it under `node_modules/@joshuafolkken/kit/` — so `prompts/review.md` resolves there when `root` has
|
|
42
|
+
// no copy, exactly as `review:brief` resolves the rubric.
|
|
43
|
+
const PACKAGE_DIRECTORY = find_package_directory(path.dirname(fileURLToPath(import.meta.url)))
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
function candidates_under(directory: string, name: string): Array<string> {
|
|
46
|
+
return [path.resolve(directory, name), entry_read_set.document_path(directory, name)]
|
|
47
|
+
}
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
function resolve_document(
|
|
50
|
+
name: string,
|
|
51
|
+
root: string = process.cwd(),
|
|
52
|
+
package_directory: string = PACKAGE_DIRECTORY,
|
|
53
|
+
): string {
|
|
54
|
+
const named = path.resolve(root, name)
|
|
55
|
+
const candidates = [...candidates_under(root, name), ...candidates_under(package_directory, name)]
|
|
44
56
|
|
|
45
|
-
return
|
|
57
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? named
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
function report_missing(markdown: string, heading: string): number {
|
|
@@ -47,9 +47,9 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
47
47
|
{ entry: 'kickoff', bytes: 36_864 },
|
|
48
48
|
{ entry: 'fullrun', bytes: 53_248 },
|
|
49
49
|
{ entry: 'halfrun', bytes: 49_152 },
|
|
50
|
-
{ entry: 'prrun', bytes:
|
|
51
|
-
{ entry: 'backlogrun', bytes:
|
|
52
|
-
{ entry: LANE_CHILD, bytes:
|
|
50
|
+
{ entry: 'prrun', bytes: 53_248 },
|
|
51
|
+
{ entry: 'backlogrun', bytes: 81_920 },
|
|
52
|
+
{ entry: LANE_CHILD, bytes: 45_056 },
|
|
53
53
|
]
|
|
54
54
|
|
|
55
55
|
function byte_size(root: string, relative_path: string): number {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { lane_instructions } from '#scripts/agent/lane-instructions'
|
|
3
4
|
import { backlogrun_parent_read_set } from './backlogrun-parent-read-set'
|
|
4
5
|
import { entry_read_set, type ReadSetCost, type SectionCost } from './entry-read-set'
|
|
5
6
|
import { lane_child_read_set } from './lane-child-read-set'
|
|
@@ -192,7 +193,9 @@ function total_lines(report: ReadSetCost, width: number): Array<string> {
|
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
function note_lines(report: ReadSetCost): Array<string> {
|
|
195
|
-
if (report.entry === lane_child_read_set.LANE_CHILD)
|
|
196
|
+
if (report.entry === lane_child_read_set.LANE_CHILD) {
|
|
197
|
+
return [...LANE_CHILD_NOTE, lane_instructions.resident_line(), '']
|
|
198
|
+
}
|
|
196
199
|
|
|
197
200
|
if (report.entry === BACKLOGRUN) return [...BACKLOGRUN_PARENT_NOTE, '']
|
|
198
201
|
|
|
@@ -14,7 +14,6 @@ import { epic_parse } from './epic-parse'
|
|
|
14
14
|
// the children the epic already names, while this one searches for issues the epic does *not* name.
|
|
15
15
|
|
|
16
16
|
const SEARCH_LIMIT = 50
|
|
17
|
-
const PARENT_MARKERS: ReadonlyArray<string> = ['親:', 'Parent:', '親:']
|
|
18
17
|
// Named apart from `epic_audit_checks.ORPHAN_CHILD`: that check reports an orphan it found, this one
|
|
19
18
|
// reports that the search for orphans was incomplete, and a reader has to be able to tell them apart.
|
|
20
19
|
const ORPHAN_SEARCH = 'orphan search'
|
|
@@ -25,18 +24,15 @@ const CAPPED_BY_MATCHES = `The open-issue scan filled its ${String(SEARCH_LIMIT)
|
|
|
25
24
|
|
|
26
25
|
const searched_issue_schema = z.object({ number: z.number(), body: z.string().nullable() })
|
|
27
26
|
|
|
28
|
-
function has_parent_marker(line: string): boolean {
|
|
29
|
-
return PARENT_MARKERS.some((marker) => line.includes(marker))
|
|
30
|
-
}
|
|
31
|
-
|
|
32
27
|
// Whether an issue's body names this epic as its parent, in the shape the issue template uses.
|
|
33
28
|
//
|
|
34
29
|
// Both halves must be on the *same line*. An issue parented to a different epic routinely backlinks
|
|
35
30
|
// this one elsewhere in its body, and matching the marker and the number independently reported
|
|
36
31
|
// every such issue as an orphan.
|
|
37
32
|
//
|
|
38
|
-
// The number is read through the same reference parse the checks use, so a parent line (
|
|
39
|
-
//
|
|
33
|
+
// The number is read through the same reference parse the checks use, so a parent line (one
|
|
34
|
+
// `epic_audit_logic.has_parent_marker` accepts) such as `owner/other#858` names that repository's
|
|
35
|
+
// epic and not this one's.
|
|
40
36
|
// The known set is this repository alone — a parent line names this epic or it names nothing here —
|
|
41
37
|
// and it is passed rather than omitted so a repository whose own name contains a dot still recognizes
|
|
42
38
|
// `joshuafolkken/site.com#858`.
|
|
@@ -49,7 +45,8 @@ function names_this_epic(line: string, epic_number: number, repo: string): boole
|
|
|
49
45
|
function claims_parent(body: string | null, epic_number: number, repo: string): boolean {
|
|
50
46
|
return (body ?? '')
|
|
51
47
|
.split('\n')
|
|
52
|
-
.
|
|
48
|
+
.filter((line) => epic_audit_logic.has_parent_marker(line))
|
|
49
|
+
.some((line) => names_this_epic(line, epic_number, repo))
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
// Why the scan stopped before the end of the open backlog, or that it did not. Both cutoffs hide the
|
|
@@ -68,9 +68,15 @@ function order_pairs(
|
|
|
68
68
|
return links.flatMap((link) => {
|
|
69
69
|
const blocker = find_child(children, { repo: epic_repo, number: link.blocker })
|
|
70
70
|
const blocked = find_child(children, { repo: epic_repo, number: link.blocked })
|
|
71
|
-
if (blocker === undefined || blocked === undefined) return []
|
|
72
71
|
|
|
73
|
-
if (
|
|
72
|
+
if (
|
|
73
|
+
blocker === undefined ||
|
|
74
|
+
blocked === undefined ||
|
|
75
|
+
!is_open_local(blocker, current_repo) ||
|
|
76
|
+
!is_open_local(blocked, current_repo)
|
|
77
|
+
) {
|
|
78
|
+
return []
|
|
79
|
+
}
|
|
74
80
|
|
|
75
81
|
// eslint-disable-next-line unicorn/no-unnecessary-array-flat-map -- one return type (sonarjs/function-return-type)
|
|
76
82
|
return [{ blocker, blocked }]
|