@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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { markdown_shape } from './markdown-shape'
|
|
2
|
+
import { report_format_reference } from './report-format-reference'
|
|
3
|
+
import { report_labels, type ReportLabels } from './report-labels'
|
|
4
|
+
|
|
5
|
+
// A lane child's closing summary carries only what GitHub does not, in a bounded length
|
|
6
|
+
// (`backlogrun-child.md` → "What the summary carries, and how long it may be"). This checks the half a
|
|
7
|
+
// machine can: the line bound, the cause / fix / result labels, and no changed-file list — a run of
|
|
8
|
+
// bullets that are a path and nothing else. What else belongs in it stays the writer's judgement.
|
|
9
|
+
|
|
10
|
+
// Two path-only bullets in a row already read as a list; one alone may be the one file a finding names.
|
|
11
|
+
const MIN_FILE_LIST_RUN = 2
|
|
12
|
+
const LIST_ITEM_PREFIX = /^\s*(?:\d+\.|[-*])\s+/u
|
|
13
|
+
const PATH_CHARACTERS = /^[\w.@/-]+$/u
|
|
14
|
+
const FILE_EXTENSION = /\.[a-z]+$/iu
|
|
15
|
+
const BACKTICK = '`'
|
|
16
|
+
// A label opens its line — after an optional list marker and bold marker — so a longer word ending
|
|
17
|
+
// in the label, or `Fix the parser` inside a sentence, does not stand in for a missing label.
|
|
18
|
+
const LABEL_LEAD = /^\s*(?:(?:\d+\.|[-*])\s+)?(?:\*\*)?/u
|
|
19
|
+
const LETTER_PATTERN = /^\p{L}/u
|
|
20
|
+
|
|
21
|
+
function opens_with_label(line: string, label: string): boolean {
|
|
22
|
+
const rest = line.replace(LABEL_LEAD, '')
|
|
23
|
+
|
|
24
|
+
return rest.startsWith(label) && !LETTER_PATTERN.test(rest.slice(label.length))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// A bullet whose whole text, backticks aside, is one path — a slash segment or a file name.
|
|
28
|
+
function is_path_item(line: string): boolean {
|
|
29
|
+
if (!LIST_ITEM_PREFIX.test(line)) return false
|
|
30
|
+
|
|
31
|
+
const item = line.replace(LIST_ITEM_PREFIX, '').replaceAll(BACKTICK, '').trim()
|
|
32
|
+
|
|
33
|
+
return PATH_CHARACTERS.test(item) && (item.includes('/') || FILE_EXTENSION.test(item))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function path_item_runs(lines: ReadonlyArray<string>): ReadonlyArray<ReadonlyArray<string>> {
|
|
37
|
+
const runs: Array<Array<string>> = [[]]
|
|
38
|
+
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
if (is_path_item(line)) runs.at(-1)?.push(line)
|
|
41
|
+
else runs.push([])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return runs.filter((run) => run.length >= MIN_FILE_LIST_RUN)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function length_violation(lines: ReadonlyArray<string>): ReadonlyArray<string> {
|
|
48
|
+
const max = report_labels.CHILD_SUMMARY_MAX_LINES
|
|
49
|
+
if (lines.length <= max) return []
|
|
50
|
+
|
|
51
|
+
return [`✖ ${String(lines.length)} lines — the summary holds at most ${String(max)}`]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function label_violations(
|
|
55
|
+
lines: ReadonlyArray<string>,
|
|
56
|
+
labels: ReportLabels,
|
|
57
|
+
): ReadonlyArray<string> {
|
|
58
|
+
return [labels.cause, labels.fix, labels.result]
|
|
59
|
+
.filter((label) => lines.every((line) => !opens_with_label(line, label)))
|
|
60
|
+
.map((label) => `✖ missing label: ${label}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function file_list_violations(lines: ReadonlyArray<string>): ReadonlyArray<string> {
|
|
64
|
+
return path_item_runs(lines).map(
|
|
65
|
+
(run) =>
|
|
66
|
+
`✖ changed-file list (${String(run.length)} path-only items) — GitHub carries it: ${run[0]?.trim() ?? ''}`,
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Every violation, in a fixed order; an empty array is a clean summary.
|
|
71
|
+
function lint_child_summary(
|
|
72
|
+
summary: string,
|
|
73
|
+
labels: ReportLabels = report_labels.session_labels(),
|
|
74
|
+
): ReadonlyArray<string> {
|
|
75
|
+
const lines = markdown_shape.content_lines(summary)
|
|
76
|
+
|
|
77
|
+
return [
|
|
78
|
+
...length_violation(lines),
|
|
79
|
+
...label_violations(lines, labels),
|
|
80
|
+
...file_list_violations(lines),
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The violations followed by one line naming where the summary's shape is written; a clean summary
|
|
85
|
+
// stays empty, so `ok` is still the only output of a pass.
|
|
86
|
+
function with_reference(violations: ReadonlyArray<string>): ReadonlyArray<string> {
|
|
87
|
+
if (violations.length === 0) return violations
|
|
88
|
+
|
|
89
|
+
return [...violations, `→ the shape: ${report_format_reference.child_summary_pointer()}`]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const report_lint_child_summary = { lint_child_summary, with_reference }
|
|
93
|
+
|
|
94
|
+
export { report_lint_child_summary }
|
|
@@ -3,24 +3,38 @@ import { text } from 'node:stream/consumers'
|
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { josh_environment_file } from '#scripts/josh/josh-environment-file'
|
|
5
5
|
import { report_lint } from './report-lint'
|
|
6
|
+
import { report_lint_child_summary } from './report-lint-child-summary'
|
|
6
7
|
|
|
7
8
|
// `josh report:lint` — read a two-layer work summary candidate from stdin and print `ok`, or the
|
|
8
|
-
// mechanical violations one per line, exiting non-zero so it works as a gate.
|
|
9
|
+
// mechanical violations one per line, exiting non-zero so it works as a gate. `--child-summary`
|
|
10
|
+
// checks a lane child's closing summary instead.
|
|
9
11
|
|
|
10
12
|
const SUCCESS_EXIT_CODE = 0
|
|
11
13
|
const FAILURE_EXIT_CODE = 1
|
|
12
14
|
const OK_MESSAGE = 'ok'
|
|
15
|
+
const CHILD_SUMMARY_FLAG = '--child-summary'
|
|
16
|
+
const ARGV_OFFSET = 2
|
|
13
17
|
const NO_INPUT_MESSAGE =
|
|
14
|
-
'✖ nothing on stdin — pipe the
|
|
18
|
+
'✖ nothing on stdin — pipe the summary in, e.g. `pnpm josh report:lint < summary.md`'
|
|
15
19
|
|
|
16
|
-
function
|
|
20
|
+
function lint(summary: string, is_child_summary: boolean): ReadonlyArray<string> {
|
|
21
|
+
if (is_child_summary) {
|
|
22
|
+
return report_lint_child_summary.with_reference(
|
|
23
|
+
report_lint_child_summary.lint_child_summary(summary),
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return report_lint.with_reference(report_lint.lint_report(summary))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function run(summary: string, argv: ReadonlyArray<string>): number {
|
|
17
31
|
if (summary.trim().length === 0) {
|
|
18
32
|
console.error(NO_INPUT_MESSAGE)
|
|
19
33
|
|
|
20
34
|
return FAILURE_EXIT_CODE
|
|
21
35
|
}
|
|
22
36
|
|
|
23
|
-
const violations =
|
|
37
|
+
const violations = lint(summary, argv.includes(CHILD_SUMMARY_FLAG))
|
|
24
38
|
|
|
25
39
|
if (violations.length === 0) {
|
|
26
40
|
console.info(OK_MESSAGE)
|
|
@@ -28,15 +42,16 @@ function run(summary: string): number {
|
|
|
28
42
|
return SUCCESS_EXIT_CODE
|
|
29
43
|
}
|
|
30
44
|
|
|
31
|
-
console.error(
|
|
45
|
+
console.error(violations.join('\n'))
|
|
32
46
|
|
|
33
47
|
return FAILURE_EXIT_CODE
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
async function read_and_run(): Promise<number> {
|
|
37
|
-
|
|
51
|
+
const argv = process.argv.slice(ARGV_OFFSET)
|
|
52
|
+
if (process.stdin.isTTY) return run('', argv)
|
|
38
53
|
|
|
39
|
-
return run(await text(process.stdin))
|
|
54
|
+
return run(await text(process.stdin), argv)
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
// `.env` is read here, on the command path alone, so the labels follow the session language.
|
|
@@ -45,4 +60,8 @@ async function main(): Promise<void> {
|
|
|
45
60
|
process.exitCode = await read_and_run()
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
const report_lint_cli = { run }
|
|
64
|
+
|
|
48
65
|
if (process.argv[1] === fileURLToPath(import.meta.url)) await main()
|
|
66
|
+
|
|
67
|
+
export { report_lint_cli }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { markdown_shape } from './markdown-shape'
|
|
1
2
|
import { report_format_reference } from './report-format-reference'
|
|
2
3
|
import { report_labels, type ReportLabels } from './report-labels'
|
|
3
4
|
|
|
@@ -40,19 +41,6 @@ const MIN_BREAK_CONDITIONS = 3
|
|
|
40
41
|
const LIST_ITEM_PATTERN = /^\s*(?:\d+\.|[-*])\s+/u
|
|
41
42
|
const REASON_SEPARATORS = /^[\s::—|、,.。-]+/u
|
|
42
43
|
|
|
43
|
-
function first_content_line(summary: string): string {
|
|
44
|
-
return (
|
|
45
|
-
summary
|
|
46
|
-
.split('\n')
|
|
47
|
-
.find((line) => line.trim().length > 0)
|
|
48
|
-
?.trim() ?? ''
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function is_fenced(summary: string): boolean {
|
|
53
|
-
return first_content_line(summary).startsWith('```')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
44
|
// A label counts only where no letter follows it, so `Change` is not found inside `Changes and tests`.
|
|
57
45
|
function has_label(summary: string, label: string): boolean {
|
|
58
46
|
return summary
|
|
@@ -111,12 +99,6 @@ function has_path_or_flag(sentence: string): boolean {
|
|
|
111
99
|
)
|
|
112
100
|
}
|
|
113
101
|
|
|
114
|
-
function fence_violation(summary: string): ReadonlyArray<string> {
|
|
115
|
-
return is_fenced(summary)
|
|
116
|
-
? ['✖ the summary is wrapped in a code fence — write it as plain markdown']
|
|
117
|
-
: []
|
|
118
|
-
}
|
|
119
|
-
|
|
120
102
|
function label_violations(summary: string, labels: ReportLabels): ReadonlyArray<string> {
|
|
121
103
|
return missing_labels(summary, labels).map((label) => `✖ missing label: ${label}`)
|
|
122
104
|
}
|
|
@@ -212,7 +194,7 @@ function lint_report(
|
|
|
212
194
|
labels: ReportLabels = report_labels.session_labels(),
|
|
213
195
|
): ReadonlyArray<string> {
|
|
214
196
|
return [
|
|
215
|
-
...fence_violation(summary),
|
|
197
|
+
...markdown_shape.fence_violation(summary, 'summary'),
|
|
216
198
|
...label_violations(summary, labels),
|
|
217
199
|
...length_violations(summary, labels),
|
|
218
200
|
...intrusion_violations(summary, labels),
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { change_base } from '#scripts/git/change-base'
|
|
4
|
+
import { git_grep_hits } from '#scripts/git/git-grep-hits'
|
|
5
|
+
import { git_spawn } from '#scripts/git/git-spawn'
|
|
6
|
+
import { COMMAND_MAP } from '#scripts/josh/josh-command-map'
|
|
7
|
+
import { test_pinned_logic, type Hunk } from '#scripts/test/test-pinned-logic'
|
|
8
|
+
import { prose_stale_scan, type Reference, type Term } from './prose-stale-scan'
|
|
9
|
+
|
|
10
|
+
// `josh prose:stale` — each word the change removed (a declared or exported name, a `josh x:y` command, an
|
|
11
|
+
// `UPPER_CASE` constant, a `.md` heading) that a `.md` line or a comment line under prompts, docs,
|
|
12
|
+
// .claude or scripts still names, as `file:line kind word`; `ok` when none (joshuafolkken/kit#5287).
|
|
13
|
+
// The diff runs from the change base to the working tree, so an uncommitted edit counts. Exit 1 on a
|
|
14
|
+
// reference, 2 when the base or a search could not be read.
|
|
15
|
+
|
|
16
|
+
const USAGE = 'usage: josh prose:stale'
|
|
17
|
+
const NO_BASE = 'prose:stale: no change base — fetch the default branch, then re-run'
|
|
18
|
+
const SEARCH_PATHS = ['*.md', 'prompts', 'docs', '.claude', 'scripts', ':(exclude)CHANGELOG.md']
|
|
19
|
+
// `process.argv` holds the runtime and the script before the command's own arguments.
|
|
20
|
+
const FIRST_ARGUMENT = 2
|
|
21
|
+
|
|
22
|
+
async function read_hunks(root: string, base: string): Promise<Array<Hunk>> {
|
|
23
|
+
const diff = await git_spawn.read([
|
|
24
|
+
'-C',
|
|
25
|
+
root,
|
|
26
|
+
'diff',
|
|
27
|
+
'-U0',
|
|
28
|
+
'--no-color',
|
|
29
|
+
'--no-ext-diff',
|
|
30
|
+
'--src-prefix=a/',
|
|
31
|
+
'--dst-prefix=b/',
|
|
32
|
+
base,
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
return test_pinned_logic.all_hunks(diff)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function reference_of(term: Term, root: string): Promise<Reference> {
|
|
39
|
+
const { target, flags } = prose_stale_scan.pattern(term)
|
|
40
|
+
const { hits, has_failed } = await git_grep_hits.search(target, flags, SEARCH_PATHS, root)
|
|
41
|
+
|
|
42
|
+
return { term, hits, has_failed }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function references(root: string, base: string): Promise<Array<Reference>> {
|
|
46
|
+
const registered = new Set(Object.keys(COMMAND_MAP))
|
|
47
|
+
const terms = prose_stale_scan.stale_terms(await read_hunks(root, base), registered)
|
|
48
|
+
|
|
49
|
+
return await Promise.all(terms.map(async (term) => await reference_of(term, root)))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function refuse(message: string): number {
|
|
53
|
+
console.error(message)
|
|
54
|
+
|
|
55
|
+
return prose_stale_scan.FAILED_EXIT
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
59
|
+
if (argv.length > 0) return refuse(USAGE)
|
|
60
|
+
|
|
61
|
+
const base = await change_base.resolved()
|
|
62
|
+
|
|
63
|
+
if (base === undefined) return refuse(NO_BASE)
|
|
64
|
+
|
|
65
|
+
const root = await git_spawn.read(['rev-parse', '--show-toplevel'])
|
|
66
|
+
const { text, exit_code } = prose_stale_scan.report(await references(root, base))
|
|
67
|
+
|
|
68
|
+
console.info(text)
|
|
69
|
+
|
|
70
|
+
return exit_code
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// A git call that throws (no repository, a diff that failed) exits 2, never 1's "found".
|
|
74
|
+
async function guarded(argv: ReadonlyArray<string>): Promise<number> {
|
|
75
|
+
try {
|
|
76
|
+
return await run(argv)
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return refuse(`prose:stale: ${String(error)}`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
83
|
+
process.exitCode = await guarded(process.argv.slice(FIRST_ARGUMENT))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const prose_stale_cli = { guarded, references, run }
|
|
87
|
+
|
|
88
|
+
export { prose_stale_cli }
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { Hit } from '#scripts/git/git-grep-hits'
|
|
2
|
+
import type { Hunk } from '#scripts/test/test-pinned-logic'
|
|
3
|
+
import { review_readers_scan } from './review-readers-scan'
|
|
4
|
+
|
|
5
|
+
// The words a diff removed that prose may still name (joshuafolkken/kit#5287) — the self-check's
|
|
6
|
+
// "Prose matches the code" (`prompts/review.md`). Pure: `prose-stale-cli.ts` reads the diff and searches.
|
|
7
|
+
//
|
|
8
|
+
// **A removed line names four kinds of word**: a name (a top-level declaration, exported or not — a
|
|
9
|
+
// namespace object's member is a plain `function` — or an `export { … }` entry) and an `UPPER_CASE`
|
|
10
|
+
// constant on a code file's removed line (the declaration reading is `review-readers-scan.ts`'s), a `josh x:y` command
|
|
11
|
+
// on any removed line, and a `.md` file's `#` heading. **A word the diff still writes is not removed**: an
|
|
12
|
+
// identifier or command any added line carries, a heading an added `.md` line still titles, and a command
|
|
13
|
+
// the registry still holds.
|
|
14
|
+
|
|
15
|
+
type TermKind = 'name' | 'constant' | 'command' | 'heading'
|
|
16
|
+
|
|
17
|
+
interface Term {
|
|
18
|
+
kind: TermKind
|
|
19
|
+
word: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Reference {
|
|
23
|
+
term: Term
|
|
24
|
+
hits: ReadonlyArray<Hit>
|
|
25
|
+
has_failed: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface Report {
|
|
29
|
+
text: string
|
|
30
|
+
exit_code: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const CODE_FILE = /\.(?:ts|js)$/u
|
|
34
|
+
const MARKDOWN_EXTENSION = '.md'
|
|
35
|
+
const COMMAND = /\bjosh\s+(?<command>[a-z][\w-]*:[\w:-]+)/gu
|
|
36
|
+
const UPPER_CASE = /^[A-Z][A-Z\d_]*[A-Z\d]$/u
|
|
37
|
+
const HEADING = /^#{1,6} (?<text>.+)$/u
|
|
38
|
+
// A command token (`review:stops`) as well as an identifier, so one set answers both kinds.
|
|
39
|
+
const TOKEN = /[\w-]+(?::[\w-]+)*/gu
|
|
40
|
+
// A comment line of a code, shell or YAML file: `//`, `/*`, a block comment's `*`, or `#`.
|
|
41
|
+
const COMMENT_LINE = /^\s*(?:\/[/*]|\*|#)/u
|
|
42
|
+
const OK_LINE = 'ok'
|
|
43
|
+
const FOUND_EXIT = 1
|
|
44
|
+
const FAILED_EXIT = 2
|
|
45
|
+
|
|
46
|
+
function to_terms(kind: TermKind, words: ReadonlyArray<string>): Array<Term> {
|
|
47
|
+
return words.map((word) => ({ kind, word }))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function code_terms(source: string): Array<Term> {
|
|
51
|
+
const declared = review_readers_scan.declarations(source).map((entry) => entry.name)
|
|
52
|
+
const listed = [...review_readers_scan.exported_names(source)]
|
|
53
|
+
const constants = declared.filter((name) => UPPER_CASE.test(name))
|
|
54
|
+
const names = [...declared.filter((name) => !UPPER_CASE.test(name)), ...listed]
|
|
55
|
+
|
|
56
|
+
return [...to_terms('name', names), ...to_terms('constant', constants)]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function command_terms(lines: ReadonlyArray<string>): Array<Term> {
|
|
60
|
+
const commands = lines.flatMap((line) =>
|
|
61
|
+
[...line.matchAll(COMMAND)].map((match) => match.groups?.['command'] ?? ''),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return to_terms('command', commands)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function headings(lines: ReadonlyArray<string>): Array<string> {
|
|
68
|
+
const texts = lines.map((line) => HEADING.exec(line)?.groups?.['text']?.trim())
|
|
69
|
+
|
|
70
|
+
return texts.filter((text): text is string => text !== undefined && text !== '')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function hunk_terms(hunk: Hunk): Array<Term> {
|
|
74
|
+
const lines = hunk.old_lines
|
|
75
|
+
const code = CODE_FILE.test(hunk.file) ? code_terms(lines.join('\n')) : []
|
|
76
|
+
const titled = hunk.file.endsWith(MARKDOWN_EXTENSION) ? to_terms('heading', headings(lines)) : []
|
|
77
|
+
|
|
78
|
+
return [...code, ...command_terms(lines), ...titled]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface Kept {
|
|
82
|
+
tokens: ReadonlySet<string>
|
|
83
|
+
titles: ReadonlySet<string>
|
|
84
|
+
registered: ReadonlySet<string>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function kept_of(hunks: ReadonlyArray<Hunk>, registered: ReadonlySet<string>): Kept {
|
|
88
|
+
const added = hunks.flatMap((hunk) => hunk.new_lines ?? [])
|
|
89
|
+
const markdown = hunks.filter((hunk) => hunk.file.endsWith(MARKDOWN_EXTENSION))
|
|
90
|
+
const titles = headings(markdown.flatMap((hunk) => hunk.new_lines ?? []))
|
|
91
|
+
const tokens = added.flatMap((line) => [...line.matchAll(TOKEN)].map((match) => match[0]))
|
|
92
|
+
|
|
93
|
+
return { tokens: new Set(tokens), titles: new Set(titles), registered }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function is_removed(term: Term, kept: Kept): boolean {
|
|
97
|
+
if (term.kind === 'heading') return !kept.titles.has(term.word)
|
|
98
|
+
if (term.kind === 'command' && kept.registered.has(term.word)) return false
|
|
99
|
+
|
|
100
|
+
return !kept.tokens.has(term.word)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Each removed word once, in diff order; `registered` is the command registry's names.
|
|
104
|
+
function stale_terms(hunks: ReadonlyArray<Hunk>, registered: ReadonlySet<string>): Array<Term> {
|
|
105
|
+
const kept = kept_of(hunks, registered)
|
|
106
|
+
const unique = new Map(hunks.flatMap((hunk) => hunk_terms(hunk)).map((term) => [term.word, term]))
|
|
107
|
+
|
|
108
|
+
return [...unique.values()].filter((term) => is_removed(term, kept))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// A heading is searched quoted, the way a `→ "Heading"` pointer names it; any other word as a whole word.
|
|
112
|
+
function pattern(term: Term): { target: string; flags: ReadonlyArray<string> } {
|
|
113
|
+
if (term.kind === 'heading') return { target: `"${term.word}"`, flags: [] }
|
|
114
|
+
|
|
115
|
+
return { target: term.word, flags: ['-w'] }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Prose only: a `.md` line, or a comment line of any other searched file.
|
|
119
|
+
function is_prose(hit: Hit): boolean {
|
|
120
|
+
return hit.path.endsWith(MARKDOWN_EXTENSION) || COMMENT_LINE.test(hit.excerpt)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function reference_lines(reference: Reference): Array<string> {
|
|
124
|
+
const { kind, word } = reference.term
|
|
125
|
+
|
|
126
|
+
if (reference.has_failed) return [`search failed: ${kind} ${word}`]
|
|
127
|
+
|
|
128
|
+
return reference.hits
|
|
129
|
+
.filter((hit) => is_prose(hit))
|
|
130
|
+
.map((hit) => `${hit.path}:${hit.line} ${kind} ${word}`)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function exit_code_of(references: ReadonlyArray<Reference>, lines: ReadonlyArray<string>): number {
|
|
134
|
+
if (references.some((reference) => reference.has_failed)) return FAILED_EXIT
|
|
135
|
+
|
|
136
|
+
return lines.length > 0 ? FOUND_EXIT : 0
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// `ok` only when every search answered and none found prose; a failed search is never read as none.
|
|
140
|
+
function report(references: ReadonlyArray<Reference>): Report {
|
|
141
|
+
const lines = references.flatMap((reference) => reference_lines(reference))
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
text: lines.length > 0 ? lines.join('\n') : OK_LINE,
|
|
145
|
+
exit_code: exit_code_of(references, lines),
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const prose_stale_scan = { FAILED_EXIT, pattern, report, stale_terms }
|
|
150
|
+
|
|
151
|
+
export type { Reference, Term }
|
|
152
|
+
export { prose_stale_scan }
|
|
@@ -32,8 +32,20 @@ import { review_checkout, type ReviewCheckout } from './review-checkout'
|
|
|
32
32
|
// **Absence is a failure, never a pass.** A fork that ignored the instruction leaves no attestation,
|
|
33
33
|
// and `check` answers `missing` rather than `ok`: the one shape this record must never take is the
|
|
34
34
|
// one the defect already had.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
//
|
|
36
|
+
// **One pointer per review slot**: `/code-review` and the interaction review run side by side in a
|
|
37
|
+
// ship (joshuafolkken/kit#4672), so a brief minted for one never drops the other's nonce, and `check`
|
|
38
|
+
// asks every slot against its own nonce. The code slot's pointer is the one the resume narrowing and
|
|
39
|
+
// the issue watch read.
|
|
40
|
+
|
|
41
|
+
const REVIEW_SLOT = { CODE: 'code', INTERACTION: 'interaction' } as const
|
|
42
|
+
const REVIEW_SLOTS = [REVIEW_SLOT.CODE, REVIEW_SLOT.INTERACTION] as const
|
|
43
|
+
type ReviewSlot = (typeof REVIEW_SLOTS)[number]
|
|
44
|
+
|
|
45
|
+
const POINTER_PREFIXES: Readonly<Record<ReviewSlot, string>> = {
|
|
46
|
+
code: 'josh-review-target-',
|
|
47
|
+
interaction: 'josh-review-interaction-target-',
|
|
48
|
+
}
|
|
37
49
|
const EXPECT_PREFIX = 'josh-review-expect-'
|
|
38
50
|
const ATTEST_PREFIX = 'josh-review-attest-'
|
|
39
51
|
|
|
@@ -64,8 +76,8 @@ interface PointerRecord {
|
|
|
64
76
|
taken_at: string
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
function pointer_path(root: string = PROJECT_ROOT): string {
|
|
68
|
-
return stamp_file.stamp_path(
|
|
79
|
+
function pointer_path(root: string = PROJECT_ROOT, slot: ReviewSlot = REVIEW_SLOT.CODE): string {
|
|
80
|
+
return stamp_file.stamp_path(POINTER_PREFIXES[slot], root)
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
function expect_path(nonce: string): string {
|
|
@@ -114,8 +126,8 @@ function to_pointer(value: unknown): PointerRecord | undefined {
|
|
|
114
126
|
return undefined
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
function read_pointer(root?: string): PointerRecord | undefined {
|
|
118
|
-
return to_pointer(read_json(pointer_path(root)))
|
|
129
|
+
function read_pointer(root?: string, slot?: ReviewSlot): PointerRecord | undefined {
|
|
130
|
+
return to_pointer(read_json(pointer_path(root, slot)))
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
function read_expectation(nonce: string): ReviewCheckout | undefined {
|
|
@@ -135,6 +147,13 @@ function drop_records(pointer: PointerRecord | undefined): void {
|
|
|
135
147
|
stamp_file.remove_stamp(attest_path(pointer.nonce))
|
|
136
148
|
}
|
|
137
149
|
|
|
150
|
+
function drop_slot(root: string | undefined, slot: ReviewSlot): void {
|
|
151
|
+
const pointer = read_pointer(root, slot)
|
|
152
|
+
|
|
153
|
+
stamp_file.remove_stamp(pointer_path(root, slot))
|
|
154
|
+
drop_records(pointer)
|
|
155
|
+
}
|
|
156
|
+
|
|
138
157
|
// An unparseable timestamp reads as stale rather than as fresh, so a corrupted record drops the
|
|
139
158
|
// requirement instead of wedging every merge in the checkout that holds it.
|
|
140
159
|
function is_stale(taken_at: string, now: number): boolean {
|
|
@@ -153,13 +172,23 @@ function is_stale(taken_at: string, now: number): boolean {
|
|
|
153
172
|
// attestation rather than accepting the one an earlier round already satisfied. Leaving the old pair
|
|
154
173
|
// on disk would only be litter, but an orphaned expectation is also a nonce a fork could still attest
|
|
155
174
|
// against, which is a contract nobody is checking.
|
|
156
|
-
|
|
175
|
+
//
|
|
176
|
+
// **A code brief opens a new round, and closes the interaction contract with it.** Only
|
|
177
|
+
// `review:brief --interaction` mints that slot, after the code brief of its round, so a contract
|
|
178
|
+
// left unattested — its reviewer died, or the fix took the change out of the interaction review's
|
|
179
|
+
// paths — would otherwise refuse every later round 2 and merge in the checkout.
|
|
180
|
+
function record_target(
|
|
181
|
+
checkout: ReviewCheckout,
|
|
182
|
+
root?: string,
|
|
183
|
+
slot: ReviewSlot = REVIEW_SLOT.CODE,
|
|
184
|
+
): string {
|
|
157
185
|
const nonce = randomBytes(NONCE_BYTES).toString('hex')
|
|
158
186
|
const taken_at = new Date().toISOString()
|
|
159
187
|
|
|
160
|
-
|
|
188
|
+
if (slot === REVIEW_SLOT.CODE) drop_slot(root, REVIEW_SLOT.INTERACTION)
|
|
189
|
+
drop_records(read_pointer(root, slot))
|
|
161
190
|
stamp_file.write_stamp(expect_path(nonce), { ...checkout, taken_at })
|
|
162
|
-
stamp_file.write_stamp(pointer_path(root), { nonce, taken_at })
|
|
191
|
+
stamp_file.write_stamp(pointer_path(root, slot), { nonce, taken_at })
|
|
163
192
|
|
|
164
193
|
return nonce
|
|
165
194
|
}
|
|
@@ -220,8 +249,8 @@ function verdict_for(nonce: string): AttestVerdict {
|
|
|
220
249
|
// tree, and evidence does not expire. Checked first, a mismatch older than eight hours would answer
|
|
221
250
|
// `not-required` and the merge would go through on exactly the review this whole record exists to
|
|
222
251
|
// catch — a run that parks, waits on CI or resumes the next morning reaches that state normally.
|
|
223
|
-
function
|
|
224
|
-
const pointer = read_pointer(root)
|
|
252
|
+
function check_slot(now: number, root: string | undefined, slot: ReviewSlot): AttestVerdict {
|
|
253
|
+
const pointer = read_pointer(root, slot)
|
|
225
254
|
|
|
226
255
|
if (pointer === undefined) return not_required()
|
|
227
256
|
|
|
@@ -232,6 +261,21 @@ function check(now: number = Date.now(), root?: string): AttestVerdict {
|
|
|
232
261
|
return verdict
|
|
233
262
|
}
|
|
234
263
|
|
|
264
|
+
function is_passing(verdict: AttestVerdict): boolean {
|
|
265
|
+
return verdict.status === 'ok' || verdict.status === not_required().status
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Every slot answers on its own nonce: the first refusal stands, else an `ok`, else `not-required`.
|
|
269
|
+
function check(now: number = Date.now(), root?: string): AttestVerdict {
|
|
270
|
+
const verdicts = REVIEW_SLOTS.map((slot) => check_slot(now, root, slot))
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
verdicts.find((verdict) => !is_passing(verdict)) ??
|
|
274
|
+
verdicts.find((verdict) => verdict.status === 'ok') ??
|
|
275
|
+
not_required()
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
235
279
|
function is_written_since(source: string, taken_at: string): boolean {
|
|
236
280
|
try {
|
|
237
281
|
const stats = lstatSync(source)
|
|
@@ -242,6 +286,18 @@ function is_written_since(source: string, taken_at: string): boolean {
|
|
|
242
286
|
}
|
|
243
287
|
}
|
|
244
288
|
|
|
289
|
+
// Whether the brief the pointer names was attested, on this branch — the half of a finished review
|
|
290
|
+
// that does not depend on where its verdict was written down.
|
|
291
|
+
function is_attested_on(checkout: Pick<ReviewCheckout, 'branch'>, root?: string): boolean {
|
|
292
|
+
const pointer = read_pointer(root)
|
|
293
|
+
|
|
294
|
+
if (pointer === undefined) return false
|
|
295
|
+
|
|
296
|
+
const verdict = verdict_for(pointer.nonce)
|
|
297
|
+
|
|
298
|
+
return verdict.status === 'ok' && verdict.expected?.branch === checkout.branch
|
|
299
|
+
}
|
|
300
|
+
|
|
245
301
|
// **Whether the last briefed review of this branch finished**. A ship resumed
|
|
246
302
|
// after a stop re-issues round 1, and without this answer it briefed the whole change again — the
|
|
247
303
|
// files the finished review had just read, re-read at full cost. It is asked before `record_target`
|
|
@@ -255,11 +311,7 @@ function is_written_since(source: string, taken_at: string): boolean {
|
|
|
255
311
|
function is_reviewed_on(checkout: ReviewCheckout, findings_path: string, root?: string): boolean {
|
|
256
312
|
const pointer = read_pointer(root)
|
|
257
313
|
|
|
258
|
-
if (pointer === undefined) return false
|
|
259
|
-
|
|
260
|
-
const verdict = verdict_for(pointer.nonce)
|
|
261
|
-
|
|
262
|
-
if (verdict.status !== 'ok' || verdict.expected?.branch !== checkout.branch) return false
|
|
314
|
+
if (pointer === undefined || !is_attested_on(checkout, root)) return false
|
|
263
315
|
|
|
264
316
|
return is_written_since(findings_path, pointer.taken_at)
|
|
265
317
|
}
|
|
@@ -286,10 +338,7 @@ async function check_here(now: number = Date.now()): Promise<AttestVerdict> {
|
|
|
286
338
|
// contract's lifetime is one run, and a record left behind would be the one the *next* run's check
|
|
287
339
|
// reads. Cleared out of turn it would drop the guard, so it is called from nowhere else.
|
|
288
340
|
function clear(root?: string): void {
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
stamp_file.remove_stamp(pointer_path(root))
|
|
292
|
-
drop_records(pointer)
|
|
341
|
+
for (const slot of REVIEW_SLOTS) drop_slot(root, slot)
|
|
293
342
|
}
|
|
294
343
|
|
|
295
344
|
// The counterpart to `check_here`, keyed the same way and for the same reason.
|
|
@@ -328,24 +377,25 @@ function refusal_message(verdict: AttestVerdict): string {
|
|
|
328
377
|
|
|
329
378
|
const review_attest = {
|
|
330
379
|
attest,
|
|
331
|
-
attest_path,
|
|
332
380
|
check,
|
|
333
381
|
check_here,
|
|
334
382
|
clear,
|
|
335
383
|
clear_here,
|
|
336
384
|
expect_path,
|
|
385
|
+
is_attested_on,
|
|
337
386
|
is_briefed_since,
|
|
338
387
|
is_reviewed_on,
|
|
339
388
|
MISMATCH_VERDICT,
|
|
340
389
|
MISSING_REASON,
|
|
341
|
-
pointer_path,
|
|
342
390
|
read_attestation,
|
|
343
391
|
read_expectation,
|
|
344
392
|
read_pointer,
|
|
345
393
|
record_target,
|
|
346
394
|
refusal_message,
|
|
395
|
+
REVIEW_SLOT,
|
|
396
|
+
REVIEW_SLOTS,
|
|
347
397
|
STALE_AFTER_MS,
|
|
348
398
|
}
|
|
349
399
|
|
|
350
|
-
export type { AttestVerdict }
|
|
400
|
+
export type { AttestVerdict, ReviewSlot }
|
|
351
401
|
export { review_attest }
|