@joshuafolkken/kit 1.1784.0 → 1.2116.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/reviewer-verification.md +19 -0
- package/.claude/agents/reviewer.md +19 -0
- package/.claude/settings.json +5 -6
- package/.claude/skills/dependency-update/SKILL.md +9 -10
- package/.claude/skills/workflow-commands/SKILL.md +6 -6
- package/.claude/skills/workflow-commands/background-commands.md +8 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +41 -32
- package/.claude/skills/workflow-commands/delegation.md +6 -6
- package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +7 -5
- package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +9 -4
- package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -2
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
- package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
- package/.claude/skills/workflow-commands/prerequisite.md +3 -2
- package/.claude/skills/workflow-commands/retrospective.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +7 -5
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-pnpm/action.yml +9 -16
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/auto-tag.yml +45 -0
- package/.github/workflows/ci.yml +155 -15
- package/.github/workflows/dependabot-auto-merge.yml +63 -4
- package/.github/workflows/production.yml +8 -0
- package/.github/workflows/publish.yml +60 -45
- package/.vscode/settings.json +19 -0
- package/CLAUDE.md +5 -12
- package/SECURITY.md +2 -2
- package/dist/CLAUDE.md +5 -12
- package/dist/commands/document-read-cli.js +2 -1
- package/dist/commands/document-section-main.js +2 -1
- package/dist/commands/read-set-cli.js +194 -134
- package/dist/commands/run-prep-main.js +9694 -10173
- package/dist/commands/run-step-cli.js +22138 -18285
- package/dist/hooks/audit-provision.js +23 -5
- package/dist/hooks/chunk-3GKE653K.js +6204 -0
- package/dist/hooks/chunk-45CJJOO7.js +1243 -0
- package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
- package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
- package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
- package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
- package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
- package/dist/hooks/chunk-VVNYJWEI.js +30 -0
- package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
- package/dist/hooks/codex-hook-adapter.js +8 -8
- package/dist/hooks/format-edited.js +4 -5
- package/dist/hooks/inputs.json +386 -360
- package/dist/hooks/pretool-guard.js +7 -6
- package/dist/hooks/session-lang.js +101 -258
- package/dist/hooks/stop-guard.js +1744 -2576
- package/dist/josh.js +7133 -6368
- package/dist/version/index.js +161 -1
- package/eslint/rules/comment-why-only.js +108 -0
- package/package.json +16 -9
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +14 -11
- package/prompts/collaboration-workflow/issue-citation.md +0 -4
- package/prompts/collaboration-workflow/issue-template.md +40 -21
- package/prompts/collaboration-workflow/lane-instructions.md +3 -1
- package/prompts/collaboration-workflow/measurement.md +8 -1
- package/prompts/collaboration-workflow/operating-rules.md +11 -2
- package/prompts/collaboration-workflow/output-bounds.md +6 -2
- package/prompts/collaboration-workflow/plan-comment.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/residency.md +3 -2
- package/prompts/collaboration-workflow/rule-delivery.md +13 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +3 -1
- package/prompts/review-rubric.md +8 -0
- package/prompts/review.md +31 -22
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-argv.ts +14 -16
- package/scripts/agent/agent-provider.ts +138 -0
- package/scripts/agent/agent-role-profile.ts +108 -156
- package/scripts/agent/agent-safe-text.ts +20 -5
- package/scripts/agent/api-usage-limit.ts +65 -0
- package/scripts/agent/claude-agent-argv.ts +19 -9
- package/scripts/agent/codex-agent-argv.ts +118 -22
- package/scripts/agent/usage-limit-reset.ts +179 -0
- package/scripts/auth/claude-token-output.ts +2 -2
- package/scripts/auto-ok/auto-ok-author.ts +53 -0
- package/scripts/auto-ok/auto-ok-cli.ts +20 -9
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +6 -1
- package/scripts/backlog/backlog-drive-hold.ts +3 -3
- package/scripts/backlog/backlog-drive-idle.ts +10 -0
- package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
- package/scripts/backlog/backlog-drive-launch.ts +8 -2
- package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
- package/scripts/backlog/backlog-drive-named.ts +28 -7
- package/scripts/backlog/backlog-drive-paused.ts +5 -4
- package/scripts/backlog/backlog-drive-restore.ts +11 -3
- package/scripts/backlog/backlog-drive.ts +21 -60
- package/scripts/backlog/backlog-gate-scope.ts +61 -0
- package/scripts/backlog/backlog-measure-close.ts +112 -0
- package/scripts/backlog/backlog-next.ts +39 -49
- package/scripts/backlog/backlog-occupied.ts +2 -2
- package/scripts/backlog/backlog-overlap.ts +42 -8
- package/scripts/backlog/backlog-plan-cli.ts +4 -1
- package/scripts/backlog/backlog-plan-comment.ts +81 -0
- package/scripts/backlog/backlog-plan-read.ts +19 -6
- package/scripts/backlog/backlog-pool.ts +50 -12
- package/scripts/backlog/backlog-rank.ts +60 -13
- package/scripts/backlog/backlog-stalled-detect.ts +2 -1
- package/scripts/backlog/backlog-waves.ts +17 -2
- package/scripts/backlog/backlogrun-launch.ts +7 -4
- package/scripts/cost-runtime/context-cut-payback.ts +27 -25
- package/scripts/cost-runtime/cost-cli.ts +37 -16
- package/scripts/cost-runtime/cost-pricing.ts +9 -0
- package/scripts/cost-runtime/cost-verdict.ts +4 -1
- package/scripts/delegation/delegation-policy.ts +2 -2
- package/scripts/delegation/duplicate-read-guard.ts +1 -0
- package/scripts/delegation/investigation-guard.ts +14 -2
- package/scripts/delegation/investigation-reads.ts +45 -23
- package/scripts/dependabot/dependabot-cooldown.ts +3 -2
- package/scripts/discovery/repo-party.ts +80 -20
- package/scripts/discovery/session-owner-pin.ts +51 -0
- package/scripts/document/capped-print.ts +1 -1
- package/scripts/document/command-reference-line-budget.ts +42 -0
- package/scripts/document/document-byte-budget.ts +17 -7
- package/scripts/document/document-checks.ts +20 -0
- package/scripts/document/document-ratchet-check.ts +101 -0
- package/scripts/document/document-section.ts +1 -0
- package/scripts/document/entry-read-budget.ts +7 -3
- package/scripts/document/issue-citation-budget.ts +94 -0
- package/scripts/epic/epic-cli-argv.ts +2 -2
- package/scripts/epic/epic-fetch.ts +4 -2
- package/scripts/epic/epic-graph.ts +7 -2
- package/scripts/epic/epic-issue.ts +3 -1
- package/scripts/epic/epic-rank.ts +5 -0
- package/scripts/exports/unused-members-check.ts +62 -0
- package/scripts/exports/unused-members-cli.ts +6 -51
- package/scripts/followup/followup-ci-path.ts +67 -0
- package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
- package/scripts/followup/followup-coderabbit-wait.ts +13 -10
- package/scripts/followup/followup-failed-tests.ts +160 -0
- package/scripts/followup/followup-job-needs.ts +100 -0
- package/scripts/followup/followup-job-rerun.ts +303 -0
- package/scripts/followup/followup-main-move.ts +113 -0
- package/scripts/followup/git-followup-finish.ts +43 -11
- package/scripts/followup/git-followup-pending.ts +3 -3
- package/scripts/followup/git-followup-stages.ts +41 -11
- package/scripts/followup/git-pr-followup-checks.ts +55 -16
- package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
- package/scripts/followup/git-pr-followup.ts +62 -27
- package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
- package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
- package/scripts/gate/busy-average.ts +34 -2
- package/scripts/gate/check-related-pinned.ts +43 -0
- package/scripts/gate/check-related-reuse.ts +64 -0
- package/scripts/gate/check-related-section.ts +57 -0
- package/scripts/gate/check-related-unplanned.ts +34 -0
- package/scripts/gate/check-related.ts +265 -0
- package/scripts/gate/command-memory.ts +1 -1
- package/scripts/gate/core-admission.ts +24 -6
- package/scripts/gate/core-budget-timing.ts +20 -6
- package/scripts/gate/core-budget.ts +40 -11
- package/scripts/gate/core-wait-record.ts +3 -2
- package/scripts/gate/core-wait.ts +25 -2
- package/scripts/gate/core-weights.ts +15 -7
- package/scripts/gate/gate-labels.ts +46 -0
- package/scripts/gate/gate-ledger.ts +14 -6
- package/scripts/gate/gate-plan.ts +103 -58
- package/scripts/gate/gate-report.ts +9 -7
- package/scripts/gate/gate-run-steps.ts +26 -3
- package/scripts/gate/gate-skip.ts +45 -6
- package/scripts/gate/gate-static-group.ts +60 -0
- package/scripts/gate/gate-steps.ts +2 -1
- package/scripts/gate/gate-tree.ts +4 -2
- package/scripts/gate/hook-gate-reuse.ts +19 -2
- package/scripts/gate/metrics-totals.ts +59 -0
- package/scripts/gate/scoped-green.ts +68 -15
- package/scripts/gate/since-green.ts +14 -6
- package/scripts/gate/structural-checks.ts +96 -0
- package/scripts/gate/verification-gate.ts +51 -16
- package/scripts/gate/verification-inputs.ts +21 -6
- package/scripts/gate/verification-tail.ts +52 -0
- package/scripts/gh/gh-author-association.ts +23 -2
- package/scripts/gh/git-ai-review-scan.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +12 -3
- package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
- package/scripts/gh/git-gh-exec.ts +32 -2
- package/scripts/gh/git-gh-issue-read.ts +4 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +8 -2
- package/scripts/gh/git-gh-issue.ts +12 -5
- package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
- package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
- package/scripts/gh/git-gh-pr-read.ts +74 -13
- package/scripts/gh/git-gh-pr-rest.ts +3 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
- package/scripts/gh/git-gh-pr-too-large.ts +20 -0
- package/scripts/gh/git-gh-pr.ts +21 -7
- package/scripts/gh/git-gh-retry.ts +141 -0
- package/scripts/gh/git-pr-ai-review.ts +15 -6
- package/scripts/gh/git-pr-checks-eval.ts +3 -0
- package/scripts/gh/git-pr-checks.ts +5 -5
- package/scripts/git/base-tree.ts +30 -0
- package/scripts/git/change-base.ts +4 -10
- package/scripts/git/changed-paths-sync.ts +63 -0
- package/scripts/git/changed-paths.ts +16 -1
- package/scripts/git/git-common-directory.ts +11 -1
- package/scripts/git/git-diff-reads.ts +66 -20
- package/scripts/git/git-error.ts +9 -2
- package/scripts/git/git-schemas.ts +13 -0
- package/scripts/git/git-worktree.ts +13 -0
- package/scripts/git/gone-branch.ts +4 -1
- package/scripts/git/main-merge-budget-conflict.ts +89 -0
- package/scripts/git/main-merge-guard.ts +55 -18
- package/scripts/git/main-merge-regenerate.ts +59 -25
- package/scripts/git/main-merge.ts +53 -5
- package/scripts/git/no-base-checkout.ts +127 -0
- package/scripts/git/stash/git-stash.ts +11 -2
- package/scripts/git/stash/stash-push-cli.ts +10 -0
- package/scripts/hooks/batch-guard.ts +40 -21
- package/scripts/hooks/edited-cspell.ts +37 -11
- package/scripts/hooks/format-edited-file.ts +22 -2
- package/scripts/hooks/lane-plan-guard.ts +50 -0
- package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
- package/scripts/hooks/pre-commit-lint.ts +14 -1
- package/scripts/hooks/pre-push-related-switch.ts +10 -0
- package/scripts/hooks/pre-push-unit.ts +76 -10
- package/scripts/hooks/pretool-guard.ts +2 -0
- package/scripts/hooks/reserved-run.ts +14 -1
- package/scripts/hooks/run-hook.sh +23 -8
- package/scripts/hooks/step-zero-notice.ts +6 -1
- package/scripts/hooks/stop-actions.ts +3 -15
- package/scripts/hooks/stop-guard.ts +7 -6
- package/scripts/init/distributed-paths.ts +1 -0
- package/scripts/init/hook-command-rewrite.ts +5 -1
- package/scripts/init/hook-launch.ts +4 -2
- package/scripts/issue/aggregation-split.ts +52 -0
- package/scripts/issue/defect-origin-line.ts +19 -1
- package/scripts/issue/defect-rate-cli.ts +5 -102
- package/scripts/issue/defect-report.ts +4 -4
- package/scripts/issue/effect-audit-cli.ts +178 -0
- package/scripts/issue/effect-audit.ts +185 -0
- package/scripts/issue/expected-effect-lint.ts +64 -9
- package/scripts/issue/git-next-issues.ts +5 -0
- package/scripts/issue/issue-body-or-comment.ts +38 -4
- package/scripts/issue/issue-cite-cli.ts +1 -3
- package/scripts/issue/issue-classification.ts +1 -0
- package/scripts/issue/issue-closing-pr.ts +8 -1
- package/scripts/issue/issue-evidence-exit.ts +4 -2
- package/scripts/issue/issue-file-cap.ts +58 -0
- package/scripts/issue/issue-file-cli.ts +111 -38
- package/scripts/issue/issue-file-deferral.ts +52 -0
- package/scripts/issue/issue-file-fold.ts +29 -10
- package/scripts/issue/issue-file-interrupt.ts +85 -0
- package/scripts/issue/issue-file-lane-hint.ts +30 -0
- package/scripts/issue/issue-file.ts +43 -6
- package/scripts/issue/issue-labels.ts +19 -2
- package/scripts/issue/issue-lint-cli.ts +27 -2
- package/scripts/issue/issue-measured-cli.ts +13 -35
- package/scripts/issue/issue-measured-close.ts +58 -0
- package/scripts/issue/issue-not-before-label.ts +2 -2
- package/scripts/issue/issue-not-before.ts +4 -1
- package/scripts/issue/issue-priority-cli.ts +60 -0
- package/scripts/issue/issue-priority-raise.ts +55 -0
- package/scripts/issue/issue-priority.ts +174 -43
- package/scripts/issue/issue-rank.ts +21 -4
- package/scripts/issue/issue-read-cli.ts +32 -6
- package/scripts/issue/issue-read.ts +37 -12
- package/scripts/issue/issue-sample-gate.ts +95 -74
- package/scripts/issue/issue-sample-prime.ts +127 -0
- package/scripts/issue/issue-sample-required.ts +57 -0
- package/scripts/issue/issue-sample-source.ts +12 -11
- package/scripts/issue/issue-search.ts +124 -0
- package/scripts/issue/issue-state.ts +13 -1
- package/scripts/issue/issue-template-section.ts +43 -0
- package/scripts/issue/issue-watch-diff.ts +31 -1
- package/scripts/issue/issue-watch-hook.ts +11 -2
- package/scripts/issue/issue-watch.ts +55 -12
- package/scripts/issue/markdown-section.ts +1 -0
- package/scripts/issue/measure-declaration.ts +121 -0
- package/scripts/issue/measure-rerun-cli.ts +7 -70
- package/scripts/issue/plan-consumers-section.ts +22 -1
- package/scripts/issue/reproduction-measure.ts +3 -1
- package/scripts/issue/size-estimate.ts +90 -0
- package/scripts/issue/trusted-measure.ts +84 -0
- package/scripts/josh/command-usage.ts +3 -2
- package/scripts/josh/durable-state-migrate.ts +105 -0
- package/scripts/josh/durable-state.ts +112 -0
- package/scripts/josh/josh-aliases.ts +95 -0
- package/scripts/josh/josh-command-map.ts +1 -82
- package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
- package/scripts/josh/josh-commands-ai.ts +5 -5
- package/scripts/josh/josh-commands-development.ts +59 -58
- package/scripts/josh/josh-commands-issue-write.ts +23 -4
- package/scripts/josh/josh-commands-issue.ts +32 -0
- package/scripts/josh/josh-commands-lane.ts +87 -4
- package/scripts/josh/josh-commands-lint.ts +16 -3
- package/scripts/josh/josh-commands-maintenance.ts +15 -0
- package/scripts/josh/josh-commands-review.ts +41 -6
- package/scripts/josh/josh-commands-ship.ts +56 -8
- package/scripts/josh/josh-commands-test-check.ts +80 -0
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/josh-environment-file.ts +43 -3
- package/scripts/josh/josh-in-process.ts +2 -2
- package/scripts/josh/josh-logic.ts +5 -3
- package/scripts/josh/josh-run.ts +19 -1
- package/scripts/josh/run-record-path.ts +15 -0
- package/scripts/josh/stamp-file.ts +11 -2
- package/scripts/lane/lane-adopt-cli.ts +98 -0
- package/scripts/lane/lane-adopt.ts +208 -0
- package/scripts/lane/lane-await.ts +5 -8
- package/scripts/lane/lane-baseline.ts +123 -0
- package/scripts/lane/lane-breakdown-cli.ts +64 -0
- package/scripts/lane/lane-breakdown-implement.ts +170 -0
- package/scripts/lane/lane-breakdown-laps.ts +107 -0
- package/scripts/lane/lane-breakdown-pinned.ts +76 -0
- package/scripts/lane/lane-breakdown-text.ts +145 -0
- package/scripts/lane/lane-breakdown.ts +239 -0
- package/scripts/lane/lane-check-stats.ts +29 -8
- package/scripts/lane/lane-child-invocation.ts +9 -2
- package/scripts/lane/lane-child-mark-keys.ts +12 -0
- package/scripts/lane/lane-child-marker.ts +20 -1
- package/scripts/lane/lane-ci-path-stats.ts +73 -0
- package/scripts/lane/lane-claim-stats.ts +84 -0
- package/scripts/lane/lane-close.ts +8 -3
- package/scripts/lane/lane-dispatch-log.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +2 -2
- package/scripts/lane/lane-gap-stats.ts +7 -6
- package/scripts/lane/lane-guard-policy.ts +7 -1
- package/scripts/lane/lane-implement-stats.ts +207 -0
- package/scripts/lane/lane-implement-subagents.ts +90 -0
- package/scripts/lane/lane-investigation-switch.ts +16 -0
- package/scripts/lane/lane-ledger-kind.ts +31 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
- package/scripts/lane/lane-ledger-schema.ts +119 -36
- package/scripts/lane/lane-ledger-write.ts +257 -0
- package/scripts/lane/lane-ledger.ts +31 -177
- package/scripts/lane/lane-load.ts +1 -1
- package/scripts/lane/lane-measure-cli.ts +176 -59
- package/scripts/lane/lane-net-read.ts +44 -0
- package/scripts/lane/lane-net-time.ts +212 -0
- package/scripts/lane/lane-open.ts +16 -7
- package/scripts/lane/lane-output.ts +2 -4
- package/scripts/lane/lane-overrun-prompt.ts +147 -0
- package/scripts/lane/lane-overrun.ts +274 -0
- package/scripts/lane/lane-paths.ts +24 -1
- package/scripts/lane/lane-pause-cli.ts +4 -1
- package/scripts/lane/lane-paused-spans.ts +97 -0
- package/scripts/lane/lane-plan-check.ts +207 -0
- package/scripts/lane/lane-plan-cli.ts +41 -0
- package/scripts/lane/lane-plan.ts +144 -0
- package/scripts/lane/lane-registry.ts +14 -1
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-round-one-stats.ts +51 -0
- package/scripts/lane/lane-sampler.ts +23 -4
- package/scripts/lane/lane-session-action.ts +93 -18
- package/scripts/lane/lane-session-broker.ts +49 -21
- package/scripts/lane/lane-session-reread-main.ts +14 -0
- package/scripts/lane/lane-session-reread.ts +51 -0
- package/scripts/lane/lane-ship-lap-stats.ts +74 -0
- package/scripts/lane/lane-sleep.ts +58 -0
- package/scripts/lane/lane-spent.ts +1 -1
- package/scripts/lane/lane-split-stats.ts +28 -0
- package/scripts/lane/lane-stage-stats.ts +13 -1
- package/scripts/lane/lane-state.ts +41 -9
- package/scripts/lane/lane-stats-tables.ts +58 -0
- package/scripts/lane/lane-stats.ts +21 -5
- package/scripts/lane/lane-stop-stats.ts +51 -19
- package/scripts/lane/lane-tail-stats.ts +32 -0
- package/scripts/lane/lane-transcript-calls.ts +67 -10
- package/scripts/lane/lane-transcript-stats.ts +44 -12
- package/scripts/lane/lane-value-cell.ts +72 -0
- package/scripts/lane/lane-value.ts +118 -0
- package/scripts/lane/lane-withdraw-closed.ts +124 -0
- package/scripts/lane/lane-withdraw.ts +108 -31
- package/scripts/lane/lane-yield.ts +85 -0
- package/scripts/lane/openai-lane-ship.ts +12 -11
- package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
- package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
- package/scripts/lane/openai-lane-supervisor.ts +5 -4
- package/scripts/lib/poll.ts +38 -9
- package/scripts/lib/settled-together.ts +10 -0
- package/scripts/lib/shell-quote.ts +21 -0
- package/scripts/lib/token-shape.ts +111 -0
- package/scripts/lint/eslint-daemon-sources.ts +79 -0
- package/scripts/lint/eslint-daemon.ts +45 -6
- package/scripts/lint/eslint-warm-cli.ts +7 -0
- package/scripts/lint/eslint-warm.ts +66 -0
- package/scripts/lint/import-graph.ts +141 -11
- package/scripts/lint/lint-gate-scope.ts +2 -2
- package/scripts/lint/lint-parallel.ts +54 -6
- package/scripts/lint/lint-related.ts +154 -33
- package/scripts/lint/rename-cli.ts +179 -0
- package/scripts/lint/rename-identifier.ts +192 -0
- package/scripts/lint/type-only-imports.ts +58 -0
- package/scripts/notify/telegram-notify.ts +12 -3
- package/scripts/observations/observation-record.ts +42 -16
- package/scripts/observations/observations-flush.ts +32 -9
- package/scripts/overrides/overrides-check.ts +9 -1
- package/scripts/release/release-approval-prune.ts +7 -1
- package/scripts/repo/required-checks-logic.ts +4 -1
- package/scripts/repo/ruleset-check.ts +4 -3
- package/scripts/report/markdown-shape.ts +28 -0
- package/scripts/report/report-format-reference.ts +15 -1
- package/scripts/report/report-labels.ts +12 -1
- package/scripts/report/report-lint-child-summary.ts +94 -0
- package/scripts/report/report-lint-cli.ts +26 -7
- package/scripts/report/report-lint.ts +2 -20
- package/scripts/review/prose-stale-cli.ts +88 -0
- package/scripts/review/prose-stale-scan.ts +152 -0
- package/scripts/review/review-attest.ts +73 -23
- package/scripts/review/review-brief-cli.ts +68 -19
- package/scripts/review/review-brief-paths.ts +22 -0
- package/scripts/review/review-brief.ts +14 -7
- package/scripts/review/review-comment-carry.ts +47 -0
- package/scripts/review/review-comment-round.ts +85 -22
- package/scripts/review/review-finding-ledger.ts +109 -7
- package/scripts/review/review-finding-text.ts +43 -0
- package/scripts/review/review-interaction.ts +11 -0
- package/scripts/review/review-lane-handover.ts +68 -0
- package/scripts/review/review-lint-cli.ts +57 -0
- package/scripts/review/review-lint-findings.ts +92 -0
- package/scripts/review/review-lint-sections.ts +80 -0
- package/scripts/review/review-lint.ts +221 -0
- package/scripts/review/review-prior-findings.ts +102 -0
- package/scripts/review/review-readers-scan.ts +4 -2
- package/scripts/review/review-record-cli.ts +177 -54
- package/scripts/review/review-record-code-round.ts +55 -0
- package/scripts/review/review-record-comment.ts +60 -0
- package/scripts/review/review-record-interaction.ts +35 -0
- package/scripts/review/review-record-pinned.ts +108 -0
- package/scripts/review/review-record.ts +25 -1
- package/scripts/review/review-reviewed-tree.ts +307 -0
- package/scripts/review/review-round-one-closure.ts +62 -0
- package/scripts/review/review-round-reuse.ts +106 -0
- package/scripts/review/review-round2-cli.ts +91 -29
- package/scripts/review/review-round2-skip-comment.ts +55 -0
- package/scripts/review/review-round2.ts +24 -8
- package/scripts/review/review-stamps.ts +7 -3
- package/scripts/review/review-stop-log.ts +20 -6
- package/scripts/review/review-stops-cli.ts +2 -1
- package/scripts/review/review-submit-cli.ts +101 -0
- package/scripts/rules/agent-model.ts +1 -1
- package/scripts/rules/bash-triggers.ts +3 -3
- package/scripts/rules/concise-reply.ts +88 -0
- package/scripts/rules/credential-body.ts +77 -0
- package/scripts/rules/decision-oracle.ts +1 -1
- package/scripts/rules/delivered-rules.ts +36 -17
- package/scripts/rules/destructive-command.ts +3 -11
- package/scripts/rules/direct-issue-comment.ts +34 -12
- package/scripts/rules/direct-issue-read.ts +191 -0
- package/scripts/rules/direct-pr-create.ts +4 -24
- package/scripts/rules/early-heartbeat.ts +2 -0
- package/scripts/rules/edit-tool.ts +27 -0
- package/scripts/rules/file-edit-rows.ts +17 -0
- package/scripts/rules/file-rewrite.ts +146 -0
- package/scripts/rules/firing-point.ts +3 -2
- package/scripts/rules/gh-api.ts +128 -7
- package/scripts/rules/gh-repo-selector.ts +84 -0
- package/scripts/rules/git-force.ts +2 -2
- package/scripts/rules/guard-remedy.ts +14 -5
- package/scripts/rules/implementation-cut.ts +61 -28
- package/scripts/rules/investigator-foreground.ts +231 -0
- package/scripts/rules/issue-citation.ts +15 -60
- package/scripts/rules/issue-comments.ts +9 -4
- package/scripts/rules/josh-git-bare.ts +1 -1
- package/scripts/rules/lane-background.ts +41 -13
- package/scripts/rules/lane-direct-tool.ts +107 -9
- package/scripts/rules/lane-interactive-ask.ts +4 -1
- package/scripts/rules/lane-park.ts +4 -2
- package/scripts/rules/lane-spilled-read.ts +90 -0
- package/scripts/rules/lane-split-park.ts +1 -1
- package/scripts/rules/lane-switch-main.ts +4 -4
- package/scripts/rules/last-prompt.ts +20 -47
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/permission-guards.ts +5 -1
- package/scripts/rules/piped-verification-shapes.ts +110 -0
- package/scripts/rules/piped-verification.ts +67 -17
- package/scripts/rules/poll-loop.ts +72 -9
- package/scripts/rules/pr-merge.ts +5 -5
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +88 -24
- package/scripts/rules/reply-language.ts +3 -17
- package/scripts/rules/reply-prose.ts +34 -0
- package/scripts/rules/repo-admin.ts +132 -0
- package/scripts/rules/reviewer-foreground.ts +88 -0
- package/scripts/rules/round-one-check-related.ts +120 -0
- package/scripts/rules/rule-body-guard.ts +5 -15
- package/scripts/rules/rule-value-cache.ts +53 -5
- package/scripts/rules/rule-value-candidates.ts +84 -0
- package/scripts/rules/rule-value-cli.ts +59 -11
- package/scripts/rules/rule-value.ts +17 -4
- package/scripts/rules/run-tail-rule.ts +1 -1
- package/scripts/rules/sed-in-place.ts +196 -0
- package/scripts/rules/shell-segments.ts +10 -2
- package/scripts/rules/stop-rule-rows.ts +1 -13
- package/scripts/rules/stop-rules.ts +33 -58
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +110 -23
- package/scripts/rules/timed-rerun.ts +1 -1
- package/scripts/rules/turn-filings.ts +90 -0
- package/scripts/rules/worktree-guard.ts +22 -7
- package/scripts/run/board/run-board-ending-reason.ts +6 -2
- package/scripts/run/board/run-board-fit.ts +24 -1
- package/scripts/run/board/run-board-found.ts +134 -0
- package/scripts/run/board/run-board-header.ts +8 -0
- package/scripts/run/board/run-board-labels.ts +11 -1
- package/scripts/run/board/run-board-lane-time.ts +1 -0
- package/scripts/run/board/run-board-link.ts +15 -2
- package/scripts/run/board/run-board-notes.ts +7 -1
- package/scripts/run/board/run-board-operation.ts +4 -2
- package/scripts/run/board/run-board-phase.ts +2 -5
- package/scripts/run/board/run-board-plan.ts +1 -1
- package/scripts/run/board/run-board-read.ts +46 -1
- package/scripts/run/board/run-board-reason.ts +20 -8
- package/scripts/run/board/run-board-render-checks.ts +3 -2
- package/scripts/run/board/run-board-render-drawn.ts +11 -0
- package/scripts/run/board/run-board-render-found.ts +157 -0
- package/scripts/run/board/run-board-render-lead.ts +9 -0
- package/scripts/run/board/run-board-render-legend.ts +19 -0
- package/scripts/run/board/run-board-render-tail.ts +1 -1
- package/scripts/run/board/run-board-render.ts +82 -18
- package/scripts/run/board/run-board-spin.ts +1 -3
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +39 -10
- package/scripts/run/carry/run-carry-ended.ts +3 -2
- package/scripts/run/carry/run-carry.ts +3 -2
- package/scripts/run/cut/run-cut-cli.ts +33 -133
- package/scripts/run/cut/run-cut-digest.ts +152 -0
- package/scripts/run/cut/run-cut-handoff.ts +52 -8
- package/scripts/run/cut/run-cut-report.ts +24 -3
- package/scripts/run/cut/run-cut-resume.ts +129 -0
- package/scripts/run/cut/run-cut-review.ts +105 -0
- package/scripts/run/cut/run-cut.ts +18 -7
- package/scripts/run/detached-launch.ts +1 -1
- package/scripts/run/entry/run-entry-cli.ts +88 -19
- package/scripts/run/entry/run-entry-spill.ts +83 -0
- package/scripts/run/entry/run-entry-stop.ts +5 -1
- package/scripts/run/entry/run-entry-trust.ts +91 -0
- package/scripts/run/event/run-event-baseline.ts +56 -0
- package/scripts/run/event/run-event-filed.ts +9 -3
- package/scripts/run/event/run-event-overrun.ts +197 -0
- package/scripts/run/event/run-event-render.ts +4 -0
- package/scripts/run/event/run-event-stream.ts +25 -3
- package/scripts/run/hold/run-stop-notify.ts +3 -8
- package/scripts/run/merge/run-merge-cap.ts +57 -0
- package/scripts/run/merge/run-merge-cli.ts +66 -23
- package/scripts/run/merge/run-merge-outcome.ts +2 -0
- package/scripts/run/merge/run-merge-ship.ts +13 -3
- package/scripts/run/merge/run-merge-signals.ts +40 -23
- package/scripts/run/merge/run-merge-steps.ts +56 -36
- package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
- package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
- package/scripts/run/merge/run-merge.ts +41 -14
- package/scripts/run/review/run-review-cli.ts +306 -20
- package/scripts/run/review/run-review-early-push.ts +121 -0
- package/scripts/run/review/run-review-join-read.ts +94 -0
- package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
- package/scripts/run/review/run-review-pinned-join.ts +216 -0
- package/scripts/run/review/run-review-pinned-steps.ts +383 -0
- package/scripts/run/review/run-review-pinned-tree.ts +37 -0
- package/scripts/run/review/run-review-pinned.ts +53 -0
- package/scripts/run/review/run-review-steps.ts +70 -34
- package/scripts/run/review/run-review.ts +26 -6
- package/scripts/run/run-parent-cut-hook.ts +3 -4
- package/scripts/run/run-prep.ts +1 -0
- package/scripts/run/run-stage-read.ts +2 -6
- package/scripts/run/run-step-cli.ts +17 -5
- package/scripts/run/run-step-lane-plan.ts +27 -0
- package/scripts/run/run-step-section.ts +26 -7
- package/scripts/run/run-step.ts +5 -2
- package/scripts/run/ship/run-ship-args.ts +19 -0
- package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
- package/scripts/run/ship/run-ship-cli.ts +131 -39
- package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
- package/scripts/run/ship/run-ship-detach.ts +10 -4
- package/scripts/run/ship/run-ship-handover.ts +106 -0
- package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
- package/scripts/run/ship/run-ship-lap.ts +63 -0
- package/scripts/run/ship/run-ship-next.ts +27 -26
- package/scripts/run/ship/run-ship-overlap.ts +8 -3
- package/scripts/run/ship/run-ship-place.ts +9 -2
- package/scripts/run/ship/run-ship-planned.ts +92 -0
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
- package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
- package/scripts/run/ship/run-ship-preflight.ts +16 -4
- package/scripts/run/ship/run-ship-probe.ts +23 -9
- package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
- package/scripts/run/ship/run-ship-relaunch.ts +51 -0
- package/scripts/run/ship/run-ship-return.ts +103 -29
- package/scripts/run/ship/run-ship-review-reason.ts +24 -0
- package/scripts/run/ship/run-ship-review-steps.ts +230 -59
- package/scripts/run/ship/run-ship-review.ts +171 -51
- package/scripts/run/ship/run-ship-reviewer.ts +138 -42
- package/scripts/run/ship/run-ship-scoped.ts +39 -10
- package/scripts/run/ship/run-ship-stage.ts +49 -13
- package/scripts/run/ship/run-ship-steps.ts +24 -4
- package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
- package/scripts/run/ship/run-ship-stop-context.ts +50 -13
- package/scripts/run/ship/run-ship-sync.ts +171 -30
- package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
- package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
- package/scripts/run/ship/run-ship-waiter.ts +114 -0
- package/scripts/run/ship/run-ship.ts +16 -2
- package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
- package/scripts/run/wake/run-wake-cli.ts +6 -113
- package/scripts/run/wake/run-wake-code.ts +53 -11
- package/scripts/run/wake/run-wake-driver.ts +5 -3
- package/scripts/run/wake/run-wake-finish.ts +137 -0
- package/scripts/run/wake/run-wake-loop.ts +5 -3
- package/scripts/run/wake/run-wake-ports.ts +11 -3
- package/scripts/run/wake/run-wake-session.ts +2 -0
- package/scripts/run/wake/run-wake.ts +3 -2
- package/scripts/safe-chain/ci-installer-pin.ts +49 -8
- package/scripts/safe-chain/project-config.ts +3 -2
- package/scripts/security/secretlint-scan.ts +42 -9
- package/scripts/security/security-audit-provision-main.ts +9 -1
- package/scripts/security/security-audit-provision.ts +1 -1
- package/scripts/security/security-audit.ts +21 -1
- package/scripts/security/sonar-file.ts +10 -5
- package/scripts/split/split-assess.ts +12 -3
- package/scripts/split/split-early-notice.ts +116 -0
- package/scripts/split/split-measure.ts +20 -2
- package/scripts/split/split-resolution.ts +107 -0
- package/scripts/test/direct-importers.ts +185 -0
- package/scripts/test/pure-files-entries.ts +83 -0
- package/scripts/test/related-heavy-tests.ts +81 -0
- package/scripts/test/test-no-base.ts +88 -0
- package/scripts/test/test-pinned-cache-logic.ts +363 -0
- package/scripts/test/test-pinned-cache.ts +337 -0
- package/scripts/test/test-pinned-emit.ts +49 -0
- package/scripts/test/test-pinned-functions.ts +116 -0
- package/scripts/test/test-pinned-ledger.ts +80 -0
- package/scripts/test/test-pinned-logic.ts +208 -20
- package/scripts/test/test-pinned-main.ts +84 -18
- package/scripts/test/test-pinned-pool.ts +73 -0
- package/scripts/test/test-pinned-probe.ts +254 -0
- package/scripts/test/test-pinned-prune-logic.ts +118 -0
- package/scripts/test/test-pinned-reach.ts +194 -0
- package/scripts/test/test-pinned-record.ts +196 -0
- package/scripts/test/test-pinned-rewire.ts +148 -0
- package/scripts/test/test-pinned-spawn.ts +81 -0
- package/scripts/test/test-pinned-stage.ts +70 -0
- package/scripts/test/test-pinned.ts +258 -47
- package/scripts/test/test-reach-cli.ts +61 -0
- package/scripts/test/test-read-map.ts +35 -11
- package/scripts/test/test-read-recording.ts +17 -5
- package/scripts/test/test-read-trace.ts +26 -8
- package/scripts/test/test-red-logic.ts +46 -7
- package/scripts/test/test-red.ts +34 -14
- package/scripts/test/test-related-claim.ts +37 -0
- package/scripts/test/test-related-main.ts +4 -1
- package/scripts/test/test-related-moved.ts +91 -0
- package/scripts/test/test-related-record.ts +43 -23
- package/scripts/test/test-related-scope.ts +99 -34
- package/scripts/test/test-related-synced.ts +135 -0
- package/scripts/test/test-related-touched.ts +84 -0
- package/scripts/test/test-related.ts +167 -83
- package/scripts/test/test-unit-guard.ts +28 -5
- package/scripts/test/unit-durations.ts +41 -0
- package/scripts/test/unit-worker-share.ts +5 -4
- package/scripts/test/vitest-cache-directory.ts +19 -0
- package/scripts/test/vitest-cli-excludes.ts +14 -0
- package/scripts/test/vitest-failure-summary.ts +111 -0
- package/scripts/test/whole-suite-files.ts +12 -1
- package/scripts/time-runtime/time-batch-guard.ts +42 -4
- package/scripts/time-runtime/time-bundle-call.ts +1 -1
- package/scripts/time-runtime/time-bundles.ts +36 -5
- package/scripts/time-runtime/time-markers.ts +2 -2
- package/scripts/time-runtime/time-search-call.ts +38 -0
- package/scripts/time-runtime/time-shell.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +17 -5
- package/scripts/version/development-engines-verdict.ts +124 -0
- package/scripts/version/latest-corepack.ts +2 -0
- package/templates/gitignore +2 -0
- package/templates/workflows/ci.yml +41 -0
- package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
- package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
- package/dist/hooks/chunk-TUIFS5E7.js +0 -72
- package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
- package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
- package/scripts/rules/filing-cap.ts +0 -139
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { run_event_stream, type RunEvent } from '#scripts/run/event/run-event-stream'
|
|
2
|
+
import { run_ship_planned } from '#scripts/run/ship/run-ship-planned'
|
|
2
3
|
import { run_ship_stage } from '#scripts/run/ship/run-ship-stage'
|
|
3
4
|
import { lane_ledger, type LedgerEntry, type StopEntry } from './lane-ledger'
|
|
4
5
|
import type { IssueAt } from './lane-spans'
|
|
@@ -44,6 +45,7 @@ const EVENT_ISSUE = /#([1-9]\d*)\b/u
|
|
|
44
45
|
// A stop recorded before it carried a cause (joshuafolkken/kit#3998) is counted under this one.
|
|
45
46
|
const UNRECORDED_CAUSE = 'unrecorded'
|
|
46
47
|
const NO_CAUSES = '-'
|
|
48
|
+
const NOTICED = ' (noticed)'
|
|
47
49
|
|
|
48
50
|
// `causes` is a preflight stop's unmet preconditions or a review stop's blocking finding kinds, every
|
|
49
51
|
// one of them, else the stop's one cause.
|
|
@@ -57,20 +59,28 @@ function is_stop(entry: LedgerEntry): entry is StopEntry {
|
|
|
57
59
|
return entry.kind === lane_ledger.KIND.STOP
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
// The issue a run event's text names first, `NaN` where it names none.
|
|
63
|
+
function issue_of(event: RunEvent): number {
|
|
64
|
+
return Number(EVENT_ISSUE.exec(event.text)?.[1])
|
|
65
|
+
}
|
|
66
|
+
|
|
60
67
|
// Every run event of `kind`, with the issue its text names first.
|
|
61
68
|
function events_of(events: ReadonlyArray<RunEvent>, kind: string): Array<IssueAt> {
|
|
62
69
|
return events
|
|
63
70
|
.filter((event) => event.kind === kind)
|
|
64
|
-
.map((event) => ({
|
|
65
|
-
issue: Number(EVENT_ISSUE.exec(event.text)?.[1]),
|
|
66
|
-
at_ms: Date.parse(event.at),
|
|
67
|
-
}))
|
|
71
|
+
.map((event) => ({ issue: issue_of(event), at_ms: Date.parse(event.at) }))
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
function parks_of(events: ReadonlyArray<RunEvent>): Array<IssueAt> {
|
|
71
75
|
return events_of(events, run_event_stream.EVENT_KIND.PARK)
|
|
72
76
|
}
|
|
73
77
|
|
|
78
|
+
function dispatches_of(entries: ReadonlyArray<LedgerEntry>): Array<IssueAt> {
|
|
79
|
+
return entries
|
|
80
|
+
.filter((entry) => is_dispatch(entry))
|
|
81
|
+
.map((entry) => ({ issue: entry.issue, at_ms: Date.parse(entry.at) }))
|
|
82
|
+
}
|
|
83
|
+
|
|
74
84
|
function ends_of(
|
|
75
85
|
entries: ReadonlyArray<LedgerEntry>,
|
|
76
86
|
parks: ReadonlyArray<IssueAt>,
|
|
@@ -78,41 +88,63 @@ function ends_of(
|
|
|
78
88
|
const resumes = entries
|
|
79
89
|
.filter((entry) => is_stage(entry))
|
|
80
90
|
.map((stage) => ({ issue: stage.issue, at_ms: lane_stage_stats.started_ms(stage) }))
|
|
81
|
-
const dispatches = entries
|
|
82
|
-
.filter((entry) => is_dispatch(entry))
|
|
83
|
-
.map((entry) => ({ issue: entry.issue, at_ms: Date.parse(entry.at) }))
|
|
84
91
|
|
|
85
|
-
return [...resumes, ...
|
|
92
|
+
return [...resumes, ...dispatches_of(entries), ...parks]
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
// A stage's start counts from the stop's own instant, so a ship relaunched within the same
|
|
89
|
-
// millisecond still ends the wait; the stage that stopped started before it.
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
// millisecond still ends the wait; the stage that stopped started before it. `josh lane:net` derives
|
|
97
|
+
// a park's and an outage's wait the same way (`lane-net-time.ts`).
|
|
98
|
+
function wait_ms(stop: IssueAt, ends: ReadonlyArray<IssueAt>): number | undefined {
|
|
92
99
|
const later = ends
|
|
93
|
-
.filter((end) => end.issue === stop.issue && end.at_ms >=
|
|
94
|
-
.map((end) => end.at_ms -
|
|
100
|
+
.filter((end) => end.issue === stop.issue && end.at_ms >= stop.at_ms)
|
|
101
|
+
.map((end) => end.at_ms - stop.at_ms)
|
|
95
102
|
|
|
96
103
|
return later.length === 0 ? undefined : Math.min(...later)
|
|
97
104
|
}
|
|
98
105
|
|
|
106
|
+
function instant_of(stop: StopEntry): IssueAt {
|
|
107
|
+
return { issue: stop.issue, at_ms: Date.parse(stop.at) }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// The `josh check:related` notices of the ship's `unplanned` answer (joshuafolkken/kit#5203).
|
|
111
|
+
function notices_of(entries: ReadonlyArray<LedgerEntry>): Array<IssueAt> {
|
|
112
|
+
return entries
|
|
113
|
+
.filter((entry) => entry.kind === lane_ledger.KIND.UNPLANNED_NOTICE)
|
|
114
|
+
.map((entry) => ({ issue: entry.issue, at_ms: Date.parse(entry.at) }))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// An `unplanned` stop the same issue's `check:related` already printed counts as `unplanned (noticed)`,
|
|
118
|
+
// so the stops the earlier notice did not prevent are a number of their own.
|
|
119
|
+
function noticed(cause: string, stop: IssueAt, notices: ReadonlyArray<IssueAt>): string {
|
|
120
|
+
if (cause !== run_ship_planned.PRECONDITION) return cause
|
|
121
|
+
const is_noticed = notices.some(
|
|
122
|
+
(notice) => notice.issue === stop.issue && notice.at_ms <= stop.at_ms,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return is_noticed ? `${cause}${NOTICED}` : cause
|
|
126
|
+
}
|
|
127
|
+
|
|
99
128
|
// A stop recorded before it named its preconditions (joshuafolkken/kit#4166) or its finding kinds
|
|
100
129
|
// (joshuafolkken/kit#4426) counts under its cause.
|
|
101
|
-
function causes_of(stop: StopEntry): ReadonlyArray<string> {
|
|
130
|
+
function causes_of(stop: StopEntry, notices: ReadonlyArray<IssueAt>): ReadonlyArray<string> {
|
|
102
131
|
const { preconditions = [], findings = [] } = stop
|
|
103
132
|
const kinds = [...preconditions, ...findings]
|
|
104
133
|
|
|
105
|
-
|
|
134
|
+
if (kinds.length === 0) return [stop.cause ?? UNRECORDED_CAUSE]
|
|
135
|
+
|
|
136
|
+
return kinds.map((kind) => noticed(kind, instant_of(stop), notices))
|
|
106
137
|
}
|
|
107
138
|
|
|
108
139
|
function waits_of(
|
|
109
140
|
entries: ReadonlyArray<LedgerEntry>,
|
|
110
141
|
ends: ReadonlyArray<IssueAt>,
|
|
142
|
+
notices: ReadonlyArray<IssueAt>,
|
|
111
143
|
): Array<StopWait> {
|
|
112
144
|
return entries.filter(is_stop).map((stop) => ({
|
|
113
145
|
reason: stop.reason,
|
|
114
|
-
causes: causes_of(stop),
|
|
115
|
-
wait_ms: wait_ms(stop, ends),
|
|
146
|
+
causes: causes_of(stop, notices),
|
|
147
|
+
wait_ms: wait_ms(instant_of(stop), ends),
|
|
116
148
|
}))
|
|
117
149
|
}
|
|
118
150
|
|
|
@@ -158,7 +190,7 @@ function table(
|
|
|
158
190
|
events: ReadonlyArray<RunEvent> = [],
|
|
159
191
|
): string {
|
|
160
192
|
const ends = ends_of(entries, parks_of(events))
|
|
161
|
-
const waits = waits_of(lane_stats.in_window(entries, window), ends)
|
|
193
|
+
const waits = waits_of(lane_stats.in_window(entries, window), ends, notices_of(entries))
|
|
162
194
|
|
|
163
195
|
return [
|
|
164
196
|
lane_stats.to_row(COLUMNS),
|
|
@@ -167,6 +199,6 @@ function table(
|
|
|
167
199
|
].join('\n')
|
|
168
200
|
}
|
|
169
201
|
|
|
170
|
-
const lane_stop_stats = { REASONS, events_of, table }
|
|
202
|
+
const lane_stop_stats = { REASONS, dispatches_of, ends_of, events_of, issue_of, table, wait_ms }
|
|
171
203
|
|
|
172
204
|
export { lane_stop_stats }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The tail of the period's lane times beside `lane:stats`'s median (joshuafolkken/kit#4910): a lane's
|
|
2
|
+
// target is a ceiling, so the lanes over the overrun threshold and the 90th percentile say what a median
|
|
3
|
+
// hides. The threshold is the one the overrun investigation launches past (`lane-overrun.ts`), which
|
|
4
|
+
// judges a lane's net time; `lane:stats` counts on its dispatch-to-merge time.
|
|
5
|
+
|
|
6
|
+
// The net minutes a lane may take before its causes are investigated.
|
|
7
|
+
const OVERRUN_LANE_MINUTES = 40
|
|
8
|
+
const TAIL_PERCENTILE = 90
|
|
9
|
+
const WHOLE_PERCENT = 100
|
|
10
|
+
|
|
11
|
+
function is_overrun(minutes: number): boolean {
|
|
12
|
+
return minutes > OVERRUN_LANE_MINUTES
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// The lanes over the threshold; one exactly at it is not over.
|
|
16
|
+
function overrun_count(minutes: ReadonlyArray<number>): number {
|
|
17
|
+
return minutes.filter((value) => is_overrun(value)).length
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The nearest-rank 90th percentile, else `undefined` with no lane.
|
|
21
|
+
function tail_minutes(minutes: ReadonlyArray<number>): number | undefined {
|
|
22
|
+
if (minutes.length === 0) return undefined
|
|
23
|
+
|
|
24
|
+
const sorted = minutes.toSorted((left, right) => left - right)
|
|
25
|
+
const rank = Math.ceil((TAIL_PERCENTILE / WHOLE_PERCENT) * sorted.length)
|
|
26
|
+
|
|
27
|
+
return sorted[rank - 1]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const lane_tail_stats = { is_overrun, OVERRUN_LANE_MINUTES, overrun_count, tail_minutes }
|
|
31
|
+
|
|
32
|
+
export { lane_tail_stats }
|
|
@@ -8,20 +8,39 @@ import { z } from 'zod'
|
|
|
8
8
|
// **A backgrounded call is kept but not timed.** Its result line is the immediate "running in the
|
|
9
9
|
// background" notice, so the gap measures nothing the run waited on; `duration_ms` is `undefined` for
|
|
10
10
|
// it, as for a call whose result never arrived (an interrupted turn).
|
|
11
|
+
//
|
|
12
|
+
// **The tool's name is kept** (`Bash`, `Edit`, …), so `josh lane:value edit` can time the edits apart
|
|
13
|
+
// from the shell calls, **and so is its lane** — the transcript directory every session of one lane
|
|
14
|
+
// (its child, its ship reviewer) writes under — so a per-lane value divides by lanes, not sessions
|
|
15
|
+
// (joshuafolkken/kit#4630).
|
|
16
|
+
//
|
|
17
|
+
// **A subagent call keeps its agent type and model** — `subagent_type`, the harness's
|
|
18
|
+
// `general-purpose` when absent, and `model` when named — so the implement's subagent time splits by
|
|
19
|
+
// the agent dispatched (joshuafolkken/kit#5297).
|
|
20
|
+
|
|
21
|
+
interface Subagent {
|
|
22
|
+
type: string
|
|
23
|
+
model: string | undefined
|
|
24
|
+
}
|
|
11
25
|
|
|
12
26
|
interface ToolCall {
|
|
13
27
|
session_id: string
|
|
28
|
+
lane: string
|
|
14
29
|
started_ms: number
|
|
15
30
|
duration_ms: number | undefined
|
|
31
|
+
tool: string | undefined
|
|
16
32
|
background: boolean
|
|
17
33
|
command: string | undefined
|
|
34
|
+
subagent: Subagent | undefined
|
|
18
35
|
output: string
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
interface ToolUse {
|
|
22
39
|
started_ms: number
|
|
40
|
+
tool: string | undefined
|
|
23
41
|
background: boolean
|
|
24
42
|
command: string | undefined
|
|
43
|
+
subagent: Subagent | undefined
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
interface ToolResult {
|
|
@@ -40,19 +59,37 @@ function join_parts(parts: ReadonlyArray<TextPart>): string {
|
|
|
40
59
|
interface RawInput {
|
|
41
60
|
command?: unknown
|
|
42
61
|
run_in_background?: unknown
|
|
62
|
+
subagent_type?: unknown
|
|
63
|
+
model?: unknown
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
interface CallInput {
|
|
46
67
|
background: boolean
|
|
47
68
|
command: string | undefined
|
|
69
|
+
subagent: Subagent
|
|
48
70
|
}
|
|
49
71
|
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
const SUBAGENT_TOOLS: ReadonlySet<string> = new Set(['Agent', 'Task'])
|
|
73
|
+
const GENERAL_PURPOSE = 'general-purpose'
|
|
74
|
+
|
|
75
|
+
function string_or_undefined(value: unknown): string | undefined {
|
|
76
|
+
return typeof value === 'string' ? value : undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// A named string, `undefined` for an empty or non-string one.
|
|
80
|
+
function named(value: unknown): string | undefined {
|
|
81
|
+
return value === '' ? undefined : string_or_undefined(value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function subagent_of(input: RawInput | null | undefined): Subagent {
|
|
85
|
+
return { type: named(input?.subagent_type) ?? GENERAL_PURPOSE, model: named(input?.model) }
|
|
86
|
+
}
|
|
52
87
|
|
|
88
|
+
function to_input(input: RawInput | null | undefined): CallInput {
|
|
53
89
|
return {
|
|
54
90
|
background: input?.run_in_background === true,
|
|
55
|
-
command:
|
|
91
|
+
command: string_or_undefined(input?.command),
|
|
92
|
+
subagent: subagent_of(input),
|
|
56
93
|
}
|
|
57
94
|
}
|
|
58
95
|
|
|
@@ -61,11 +98,19 @@ const TEXT_PARTS_SCHEMA = z.array(z.object({ text: z.string().nullish() }))
|
|
|
61
98
|
const RESULT_CONTENT_SCHEMA = z
|
|
62
99
|
.union([z.string(), TEXT_PARTS_SCHEMA.transform(join_parts)])
|
|
63
100
|
.catch('')
|
|
64
|
-
const INPUT_SCHEMA = z
|
|
101
|
+
const INPUT_SCHEMA = z
|
|
102
|
+
.object({
|
|
103
|
+
command: z.unknown(),
|
|
104
|
+
run_in_background: z.unknown(),
|
|
105
|
+
subagent_type: z.unknown(),
|
|
106
|
+
model: z.unknown(),
|
|
107
|
+
})
|
|
108
|
+
.partial()
|
|
65
109
|
|
|
66
110
|
const BLOCK_SCHEMA = z.object({
|
|
67
111
|
type: z.string(),
|
|
68
112
|
id: z.string().nullish(),
|
|
113
|
+
name: z.string().nullish(),
|
|
69
114
|
input: INPUT_SCHEMA.nullish().transform(to_input),
|
|
70
115
|
tool_use_id: z.string().nullish(),
|
|
71
116
|
content: RESULT_CONTENT_SCHEMA.optional(),
|
|
@@ -91,10 +136,17 @@ interface Pairs {
|
|
|
91
136
|
results: Map<string, ToolResult>
|
|
92
137
|
}
|
|
93
138
|
|
|
139
|
+
// The agent a subagent call dispatched; any other tool's input names none.
|
|
140
|
+
function subagent_in(tool: string | undefined, block: Block): Subagent | undefined {
|
|
141
|
+
return SUBAGENT_TOOLS.has(tool ?? '') ? block.input.subagent : undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
94
144
|
function record_use(pairs: Pairs, block: Block, started_ms: number): void {
|
|
95
145
|
if (block.type !== 'tool_use' || !block.id) return
|
|
96
146
|
|
|
97
|
-
|
|
147
|
+
const tool = block.name ?? undefined
|
|
148
|
+
|
|
149
|
+
pairs.uses.set(block.id, { started_ms, tool, ...block.input, subagent: subagent_in(tool, block) })
|
|
98
150
|
}
|
|
99
151
|
|
|
100
152
|
function record_result(pairs: Pairs, block: Block, ended_ms: number): void {
|
|
@@ -116,30 +168,35 @@ function record_line(pairs: Pairs, line: string): void {
|
|
|
116
168
|
}
|
|
117
169
|
}
|
|
118
170
|
|
|
119
|
-
|
|
171
|
+
// The transcript a call was read from: its session, and the lane that session belongs to.
|
|
172
|
+
type CallSource = Pick<ToolCall, 'session_id' | 'lane'>
|
|
173
|
+
|
|
174
|
+
function to_call(source: CallSource, use: ToolUse, result: ToolResult | undefined): ToolCall {
|
|
120
175
|
const is_timed = result !== undefined && !use.background
|
|
121
176
|
|
|
122
177
|
return {
|
|
123
|
-
|
|
178
|
+
...source,
|
|
124
179
|
started_ms: use.started_ms,
|
|
125
180
|
duration_ms: is_timed ? result.ended_ms - use.started_ms : undefined,
|
|
181
|
+
tool: use.tool,
|
|
126
182
|
background: use.background,
|
|
127
183
|
command: use.command,
|
|
184
|
+
subagent: use.subagent,
|
|
128
185
|
output: result?.output ?? '',
|
|
129
186
|
}
|
|
130
187
|
}
|
|
131
188
|
|
|
132
189
|
// Every tool call `raw` — one transcript's text — records, in the order the calls were made.
|
|
133
|
-
function parse(
|
|
190
|
+
function parse(source: CallSource, raw: string): Array<ToolCall> {
|
|
134
191
|
const pairs: Pairs = { uses: new Map(), results: new Map() }
|
|
135
192
|
const lines = raw.split('\n')
|
|
136
193
|
|
|
137
194
|
for (const line of lines) record_line(pairs, line)
|
|
138
195
|
|
|
139
|
-
return [...pairs.uses].map(([id, use]) => to_call(
|
|
196
|
+
return [...pairs.uses].map(([id, use]) => to_call(source, use, pairs.results.get(id)))
|
|
140
197
|
}
|
|
141
198
|
|
|
142
199
|
const lane_transcript_calls = { parse }
|
|
143
200
|
|
|
144
|
-
export type { ToolCall }
|
|
201
|
+
export type { Subagent, ToolCall }
|
|
145
202
|
export { lane_transcript_calls }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
1
2
|
import { cost_transcript } from '#scripts/cost-runtime/cost-transcript'
|
|
2
3
|
import { MS_PER_SECOND } from '#scripts/lib/timeouts'
|
|
3
4
|
import { time_shell } from '#scripts/time-runtime/time-shell'
|
|
@@ -65,11 +66,16 @@ function segment_command(segment: string): string {
|
|
|
65
66
|
return named.slice(time_shell.JOSH_PREFIX.length)
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
// Every josh command a shell `command` runs, in order.
|
|
70
|
+
function josh_commands(command = ''): Array<string> {
|
|
69
71
|
const spoken = time_shell.unquoted(command)
|
|
70
72
|
const named = spoken.split(SEPARATOR_PATTERN).map((segment) => segment_command(segment))
|
|
71
73
|
|
|
72
|
-
return named.filter((name) =>
|
|
74
|
+
return named.filter((name) => name !== '')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function verification_commands(command = ''): Array<string> {
|
|
78
|
+
return josh_commands(command).filter((name) => VERIFICATION_COMMANDS.has(name))
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
function seconds_cell(ms: number | undefined): string {
|
|
@@ -84,11 +90,14 @@ function durations(calls: ReadonlyArray<ToolCall>): Array<number> {
|
|
|
84
90
|
return calls.map((call) => call.duration_ms).filter((ms) => is_timed(ms))
|
|
85
91
|
}
|
|
86
92
|
|
|
93
|
+
// The lane sessions that made a call among `calls`, each once.
|
|
94
|
+
function sessions_of(calls: ReadonlyArray<ToolCall>): Array<string> {
|
|
95
|
+
return [...new Set(calls.map((call) => call.session_id))]
|
|
96
|
+
}
|
|
97
|
+
|
|
87
98
|
// Each session's summed foreground tool time, for the sessions that made a call in the period.
|
|
88
99
|
function session_times(calls: ReadonlyArray<ToolCall>): Array<number> {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return sessions.map((session) =>
|
|
100
|
+
return sessions_of(calls).map((session) =>
|
|
92
101
|
durations(calls.filter((call) => call.session_id === session)).reduce(
|
|
93
102
|
(sum, value) => sum + value,
|
|
94
103
|
0,
|
|
@@ -102,12 +111,17 @@ function share_cell(part: number, whole: number): string {
|
|
|
102
111
|
return `${String(part)} of ${String(whole)} (${((part / whole) * PERCENT).toFixed(1)}%)`
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
// The calls that ran the verification command `name` and no other, so each one's time is its own.
|
|
115
|
+
function solo_calls(calls: ReadonlyArray<ToolCall>, name: string): Array<ToolCall> {
|
|
116
|
+
return calls.filter((call) => {
|
|
107
117
|
const named = verification_commands(call.command)
|
|
108
118
|
|
|
109
119
|
return named.length === 1 && named[0] === name
|
|
110
120
|
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function solo_median_row(verifying: ReadonlyArray<ToolCall>, name: string): Array<string> {
|
|
124
|
+
const solo = solo_calls(verifying, name)
|
|
111
125
|
|
|
112
126
|
return [`${name} median, solo (s)`, seconds_cell(lane_stats.median(durations(solo)))]
|
|
113
127
|
}
|
|
@@ -140,11 +154,15 @@ function rows_of(calls: ReadonlyArray<ToolCall>): Array<Array<string>> {
|
|
|
140
154
|
]
|
|
141
155
|
}
|
|
142
156
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const period = calls.filter(
|
|
157
|
+
function in_period(calls: ReadonlyArray<ToolCall>, window: StatsWindow): Array<ToolCall> {
|
|
158
|
+
return calls.filter(
|
|
146
159
|
(call) => call.started_ms >= window.since_ms && call.started_ms <= window.until_ms,
|
|
147
160
|
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// The period's table: `calls` is every lane session's calls, filtered here to `window`.
|
|
164
|
+
function table(calls: ReadonlyArray<ToolCall>, window: StatsWindow): string {
|
|
165
|
+
const period = in_period(calls, window)
|
|
148
166
|
|
|
149
167
|
return [
|
|
150
168
|
lane_stats.to_row(COLUMNS),
|
|
@@ -156,13 +174,27 @@ function table(calls: ReadonlyArray<ToolCall>, window: StatsWindow): string {
|
|
|
156
174
|
// The tool calls of every lane session under `directories` that was still written to in `window` — a
|
|
157
175
|
// transcript last modified before the period holds no call in it, so it is never read. A delegated
|
|
158
176
|
// unit's transcript is not a lane session, and its time is already inside the parent's wait for it.
|
|
177
|
+
// A session's own transcript sits directly in its lane's directory, so that directory is its lane.
|
|
159
178
|
function read_calls(directories: ReadonlyArray<string>, window: StatsWindow): Array<ToolCall> {
|
|
160
179
|
return cost_transcript
|
|
161
180
|
.list_sessions_across(directories)
|
|
162
181
|
.filter((file) => !file.is_delegated && file.modified_ms >= window.since_ms)
|
|
163
|
-
.flatMap((file) =>
|
|
182
|
+
.flatMap((file) =>
|
|
183
|
+
lane_transcript_calls.parse(
|
|
184
|
+
{ session_id: file.session_id, lane: path.dirname(file.path) },
|
|
185
|
+
cost_transcript.read_raw(file),
|
|
186
|
+
),
|
|
187
|
+
)
|
|
164
188
|
}
|
|
165
189
|
|
|
166
|
-
const lane_transcript_stats = {
|
|
190
|
+
const lane_transcript_stats = {
|
|
191
|
+
durations,
|
|
192
|
+
in_period,
|
|
193
|
+
josh_commands,
|
|
194
|
+
read_calls,
|
|
195
|
+
solo_calls,
|
|
196
|
+
table,
|
|
197
|
+
verification_commands,
|
|
198
|
+
}
|
|
167
199
|
|
|
168
200
|
export { lane_transcript_stats }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// `josh lane:value cell --table <title> --row <label> --column <header>` — one cell of a table
|
|
2
|
+
// `josh lane:stats` prints, as one number (joshuafolkken/kit#5144). The tables are read as printed, so
|
|
3
|
+
// every new `lane:stats` table is declarable without a new metric and no aggregation is copied.
|
|
4
|
+
//
|
|
5
|
+
// A table is named by its first header cell and a row by its first cell. **The address must name one
|
|
6
|
+
// cell** — two tables share the `check` title, so an address that matches more than one numeric cell
|
|
7
|
+
// answers `undefined`, as a missing or non-numeric one does, never a guess.
|
|
8
|
+
|
|
9
|
+
const ROW_OPEN = '| '
|
|
10
|
+
const ROW_CLOSE = ' |'
|
|
11
|
+
const CELL_SEPARATOR = ' | '
|
|
12
|
+
const BLOCK_SEPARATOR = /\n\s*\n/u
|
|
13
|
+
const NUMBER_PATTERN = /^-?\d+(?:\.\d+)?$/u
|
|
14
|
+
const HEADER_ROWS = 2
|
|
15
|
+
|
|
16
|
+
interface CellAddress {
|
|
17
|
+
table: string
|
|
18
|
+
row: string
|
|
19
|
+
column: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type Cells = ReadonlyArray<string>
|
|
23
|
+
|
|
24
|
+
function cells_of(line: string): Cells | undefined {
|
|
25
|
+
const text = line.trim()
|
|
26
|
+
|
|
27
|
+
if (!text.startsWith(ROW_OPEN) || !text.endsWith(ROW_CLOSE)) return undefined
|
|
28
|
+
|
|
29
|
+
return text.slice(ROW_OPEN.length, -ROW_CLOSE.length).split(CELL_SEPARATOR)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function rows_of(block: string): ReadonlyArray<Cells> {
|
|
33
|
+
return block
|
|
34
|
+
.split('\n')
|
|
35
|
+
.map((line) => cells_of(line))
|
|
36
|
+
.filter((row) => row !== undefined)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Every printed table as its rows of cells, the header first; a block with no row is dropped.
|
|
40
|
+
function blocks_of(tables: ReadonlyArray<string>): ReadonlyArray<ReadonlyArray<Cells>> {
|
|
41
|
+
return tables
|
|
42
|
+
.flatMap((table) => table.split(BLOCK_SEPARATOR))
|
|
43
|
+
.map((block) => rows_of(block))
|
|
44
|
+
.filter((rows) => rows.length > 0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The cell texts at the address in one table — none when its title or column differs.
|
|
48
|
+
function texts_in(rows: ReadonlyArray<Cells>, address: CellAddress): ReadonlyArray<string> {
|
|
49
|
+
const [header = []] = rows
|
|
50
|
+
const column = header.indexOf(address.column)
|
|
51
|
+
|
|
52
|
+
if (header[0] !== address.table || column < 1) return []
|
|
53
|
+
|
|
54
|
+
return rows
|
|
55
|
+
.slice(HEADER_ROWS)
|
|
56
|
+
.filter((cells) => cells[0] === address.row)
|
|
57
|
+
.map((cells) => cells[column] ?? '')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The one numeric cell at `address` across `tables`, or `undefined` for none, many or a non-number.
|
|
61
|
+
function value(tables: ReadonlyArray<string>, address: CellAddress): number | undefined {
|
|
62
|
+
const numbers = blocks_of(tables)
|
|
63
|
+
.flatMap((rows) => texts_in(rows, address))
|
|
64
|
+
.filter((text) => NUMBER_PATTERN.test(text))
|
|
65
|
+
|
|
66
|
+
return numbers.length === 1 ? Number(numbers[0]) : undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const lane_value_cell = { value }
|
|
70
|
+
|
|
71
|
+
export type { CellAddress }
|
|
72
|
+
export { lane_value_cell }
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { metrics_totals } from '#scripts/gate/metrics-totals'
|
|
2
|
+
import { since_green } from '#scripts/gate/since-green'
|
|
3
|
+
import { MS_PER_MINUTE, MS_PER_SECOND } from '#scripts/lib/timeouts'
|
|
4
|
+
import { run_ship_pre_detach } from '#scripts/run/ship/run-ship-pre-detach'
|
|
5
|
+
import { lane_ledger, type CoreWaitEntry, type LedgerEntry } from './lane-ledger'
|
|
6
|
+
import { lane_stats, type StatsWindow } from './lane-stats'
|
|
7
|
+
import type { ToolCall } from './lane-transcript-calls'
|
|
8
|
+
import { lane_transcript_stats } from './lane-transcript-stats'
|
|
9
|
+
|
|
10
|
+
// `josh lane:value <metric> --since <instant>` — one lane measurement as one number, so a follow-up
|
|
11
|
+
// measurement Issue can declare it (its measurement declaration line) and `backlog:drive` closes it without a lane
|
|
12
|
+
// (joshuafolkken/kit#4630). Each metric is one an Issue measured by hand from the lane transcripts or
|
|
13
|
+
// the ledger; `lane:stats` prints the tables they were read from.
|
|
14
|
+
//
|
|
15
|
+
// **Per lane is per lane, not per session** — every lane runs at least a child and a ship-reviewer
|
|
16
|
+
// session, so the divisor is the lanes whose sessions made a tool call in the period. A metric with no
|
|
17
|
+
// sample answers `undefined`, never a zero that would pass.
|
|
18
|
+
|
|
19
|
+
const LINT_RELATED = 'lint:related'
|
|
20
|
+
const SHIP = 'ship'
|
|
21
|
+
const EDIT_TOOL = 'Edit'
|
|
22
|
+
// Both pre-detach forms: a `--since` window from before joshuafolkken/kit#4716 holds the bare form's.
|
|
23
|
+
const METRICS_STOPS: ReadonlyArray<string> = [
|
|
24
|
+
metrics_totals.METRICS_RERECORD_COMMAND,
|
|
25
|
+
metrics_totals.METRICS_TOTALS_COMMAND,
|
|
26
|
+
].map((list) => run_ship_pre_detach.failure_prefix(list))
|
|
27
|
+
|
|
28
|
+
interface LaneRecords {
|
|
29
|
+
entries: ReadonlyArray<LedgerEntry>
|
|
30
|
+
calls: ReadonlyArray<ToolCall>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Metric = (records: LaneRecords) => number | undefined
|
|
34
|
+
|
|
35
|
+
function seconds_of(ms: number | undefined): number | undefined {
|
|
36
|
+
return ms === undefined ? undefined : ms / MS_PER_SECOND
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// `count` spread over the period's lanes, or `undefined` when no lane ran.
|
|
40
|
+
function per_lane(count: number, calls: ReadonlyArray<ToolCall>): number | undefined {
|
|
41
|
+
const lanes = new Set(calls.map((call) => call.lane)).size
|
|
42
|
+
|
|
43
|
+
return lanes === 0 ? undefined : count / lanes
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// #4599: the median time of a `lint:related` that found nothing moved since its last green run.
|
|
47
|
+
function lint_unmoved({ calls }: LaneRecords): number | undefined {
|
|
48
|
+
const unmoved = lane_transcript_stats
|
|
49
|
+
.solo_calls(calls, LINT_RELATED)
|
|
50
|
+
.filter((call) => call.output.includes(since_green.UNMOVED_REASON))
|
|
51
|
+
|
|
52
|
+
return seconds_of(lane_stats.median(lane_transcript_stats.durations(unmoved)))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// #4600: the `ship` calls whose pre-detach check the metrics totals stopped, per lane.
|
|
56
|
+
function metrics_stops({ calls }: LaneRecords): number | undefined {
|
|
57
|
+
const stops = calls.filter(
|
|
58
|
+
(call) =>
|
|
59
|
+
lane_transcript_stats.verification_commands(call.command).includes(SHIP) &&
|
|
60
|
+
METRICS_STOPS.some((stop) => call.output.includes(stop)),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return per_lane(stops.length, calls)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A lane's `lint:related` wait — one outside a lane (the main checkout's) has no `issue` and no lane
|
|
67
|
+
// session to be divided among.
|
|
68
|
+
function is_lint_wait(entry: LedgerEntry): entry is CoreWaitEntry {
|
|
69
|
+
return (
|
|
70
|
+
entry.kind === lane_ledger.KIND.CORE_WAIT &&
|
|
71
|
+
entry.command === LINT_RELATED &&
|
|
72
|
+
entry.issue !== undefined
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// #4601: the minutes `lint:related` waited for the core budget, per lane.
|
|
77
|
+
function lint_core_wait({ entries, calls }: LaneRecords): number | undefined {
|
|
78
|
+
const waited = entries.filter((entry) => is_lint_wait(entry))
|
|
79
|
+
const total_ms = waited.reduce((sum, wait) => sum + wait.waited_ms, 0)
|
|
80
|
+
|
|
81
|
+
return per_lane(total_ms / MS_PER_MINUTE, calls)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// #4602: the median time of one `Edit` call, hooks included.
|
|
85
|
+
function edit({ calls }: LaneRecords): number | undefined {
|
|
86
|
+
const edits = calls.filter((call) => call.tool === EDIT_TOOL)
|
|
87
|
+
|
|
88
|
+
return seconds_of(lane_stats.median(lane_transcript_stats.durations(edits)))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const METRICS: Readonly<Record<string, Metric>> = {
|
|
92
|
+
'lint-unmoved': lint_unmoved,
|
|
93
|
+
'metrics-stops': metrics_stops,
|
|
94
|
+
'lint-core-wait': lint_core_wait,
|
|
95
|
+
edit,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const NAMES: ReadonlyArray<string> = Object.keys(METRICS)
|
|
99
|
+
|
|
100
|
+
function is_metric(name: string): boolean {
|
|
101
|
+
return Object.hasOwn(METRICS, name)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The value of `name` over `window`; `records` are the whole ledger and every lane call, cut here.
|
|
105
|
+
function value(name: string, records: LaneRecords, window: StatsWindow): number | undefined {
|
|
106
|
+
const metric = is_metric(name) ? METRICS[name] : undefined
|
|
107
|
+
const period = {
|
|
108
|
+
entries: lane_stats.in_window(records.entries, window),
|
|
109
|
+
calls: lane_transcript_stats.in_period(records.calls, window),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return metric === undefined ? undefined : metric(period)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const lane_value = { NAMES, is_metric, value }
|
|
116
|
+
|
|
117
|
+
export type { LaneRecords }
|
|
118
|
+
export { lane_value }
|