@joshuafolkken/kit 1.1784.0 → 1.2114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/.claude/agents/reviewer-verification.md +19 -0
  2. package/.claude/agents/reviewer.md +19 -0
  3. package/.claude/settings.json +5 -6
  4. package/.claude/skills/dependency-update/SKILL.md +9 -10
  5. package/.claude/skills/workflow-commands/SKILL.md +6 -6
  6. package/.claude/skills/workflow-commands/background-commands.md +8 -5
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
  13. package/.claude/skills/workflow-commands/backlogrun.md +1 -1
  14. package/.claude/skills/workflow-commands/chain-rule.md +41 -32
  15. package/.claude/skills/workflow-commands/delegation.md +6 -6
  16. package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
  17. package/.claude/skills/workflow-commands/followup.md +7 -5
  18. package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
  19. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  20. package/.claude/skills/workflow-commands/issue-comments.md +9 -4
  21. package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
  22. package/.claude/skills/workflow-commands/kickoff.md +2 -2
  23. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  24. package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
  25. package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
  26. package/.claude/skills/workflow-commands/prerequisite.md +3 -2
  27. package/.claude/skills/workflow-commands/retrospective.md +2 -2
  28. package/.claude/skills/workflow-commands/split-assessment.md +7 -5
  29. package/.codex/hooks.json +5 -5
  30. package/.github/actions/setup-pnpm/action.yml +9 -16
  31. package/.github/dependabot.yml +16 -0
  32. package/.github/workflows/auto-tag.yml +45 -0
  33. package/.github/workflows/ci.yml +155 -15
  34. package/.github/workflows/dependabot-auto-merge.yml +63 -4
  35. package/.github/workflows/production.yml +8 -0
  36. package/.github/workflows/publish.yml +59 -45
  37. package/.vscode/settings.json +19 -0
  38. package/CLAUDE.md +5 -12
  39. package/SECURITY.md +2 -2
  40. package/dist/CLAUDE.md +5 -12
  41. package/dist/commands/document-read-cli.js +2 -1
  42. package/dist/commands/document-section-main.js +2 -1
  43. package/dist/commands/read-set-cli.js +194 -134
  44. package/dist/commands/run-prep-main.js +9694 -10173
  45. package/dist/commands/run-step-cli.js +22138 -18285
  46. package/dist/hooks/audit-provision.js +23 -5
  47. package/dist/hooks/chunk-3GKE653K.js +6204 -0
  48. package/dist/hooks/chunk-45CJJOO7.js +1243 -0
  49. package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
  50. package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
  51. package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
  52. package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
  53. package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
  54. package/dist/hooks/chunk-VVNYJWEI.js +30 -0
  55. package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
  56. package/dist/hooks/codex-hook-adapter.js +8 -8
  57. package/dist/hooks/format-edited.js +4 -5
  58. package/dist/hooks/inputs.json +415 -389
  59. package/dist/hooks/pretool-guard.js +7 -6
  60. package/dist/hooks/session-lang.js +101 -258
  61. package/dist/hooks/stop-guard.js +1744 -2576
  62. package/dist/josh.js +7133 -6368
  63. package/dist/version/index.js +161 -1
  64. package/eslint/rules/comment-why-only.js +108 -0
  65. package/package.json +15 -8
  66. package/prompts/coding-standards.md +1 -1
  67. package/prompts/collaboration-workflow/file-edits.md +14 -11
  68. package/prompts/collaboration-workflow/issue-citation.md +0 -4
  69. package/prompts/collaboration-workflow/issue-template.md +40 -21
  70. package/prompts/collaboration-workflow/lane-instructions.md +3 -1
  71. package/prompts/collaboration-workflow/measurement.md +8 -1
  72. package/prompts/collaboration-workflow/operating-rules.md +11 -2
  73. package/prompts/collaboration-workflow/output-bounds.md +6 -2
  74. package/prompts/collaboration-workflow/plan-comment.md +1 -1
  75. package/prompts/collaboration-workflow/principles.md +9 -0
  76. package/prompts/collaboration-workflow/residency.md +3 -2
  77. package/prompts/collaboration-workflow/rule-delivery.md +13 -1
  78. package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
  79. package/prompts/collaboration-workflow/wip-cap.md +3 -1
  80. package/prompts/review-rubric.md +8 -0
  81. package/prompts/review.md +31 -22
  82. package/prompts/testing-guide.md +1 -0
  83. package/scripts/agent/agent-argv.ts +14 -16
  84. package/scripts/agent/agent-provider.ts +138 -0
  85. package/scripts/agent/agent-role-profile.ts +108 -156
  86. package/scripts/agent/agent-safe-text.ts +20 -5
  87. package/scripts/agent/api-usage-limit.ts +65 -0
  88. package/scripts/agent/claude-agent-argv.ts +19 -9
  89. package/scripts/agent/codex-agent-argv.ts +118 -22
  90. package/scripts/agent/usage-limit-reset.ts +179 -0
  91. package/scripts/auth/claude-token-output.ts +2 -2
  92. package/scripts/auto-ok/auto-ok-author.ts +53 -0
  93. package/scripts/auto-ok/auto-ok-cli.ts +20 -9
  94. package/scripts/backlog/backlog-defect-priority.ts +1 -1
  95. package/scripts/backlog/backlog-drive-cli.ts +6 -1
  96. package/scripts/backlog/backlog-drive-hold.ts +3 -3
  97. package/scripts/backlog/backlog-drive-idle.ts +10 -0
  98. package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
  99. package/scripts/backlog/backlog-drive-launch.ts +8 -2
  100. package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
  101. package/scripts/backlog/backlog-drive-named.ts +28 -7
  102. package/scripts/backlog/backlog-drive-paused.ts +5 -4
  103. package/scripts/backlog/backlog-drive-restore.ts +11 -3
  104. package/scripts/backlog/backlog-drive.ts +21 -60
  105. package/scripts/backlog/backlog-gate-scope.ts +61 -0
  106. package/scripts/backlog/backlog-measure-close.ts +112 -0
  107. package/scripts/backlog/backlog-next.ts +39 -49
  108. package/scripts/backlog/backlog-occupied.ts +2 -2
  109. package/scripts/backlog/backlog-overlap.ts +42 -8
  110. package/scripts/backlog/backlog-plan-cli.ts +4 -1
  111. package/scripts/backlog/backlog-plan-comment.ts +81 -0
  112. package/scripts/backlog/backlog-plan-read.ts +19 -6
  113. package/scripts/backlog/backlog-pool.ts +50 -12
  114. package/scripts/backlog/backlog-rank.ts +60 -13
  115. package/scripts/backlog/backlog-stalled-detect.ts +2 -1
  116. package/scripts/backlog/backlog-waves.ts +17 -2
  117. package/scripts/backlog/backlogrun-launch.ts +7 -4
  118. package/scripts/cost-runtime/context-cut-payback.ts +27 -25
  119. package/scripts/cost-runtime/cost-cli.ts +37 -16
  120. package/scripts/cost-runtime/cost-pricing.ts +9 -0
  121. package/scripts/cost-runtime/cost-verdict.ts +4 -1
  122. package/scripts/delegation/delegation-policy.ts +2 -2
  123. package/scripts/delegation/duplicate-read-guard.ts +1 -0
  124. package/scripts/delegation/investigation-guard.ts +14 -2
  125. package/scripts/delegation/investigation-reads.ts +45 -23
  126. package/scripts/dependabot/dependabot-cooldown.ts +3 -2
  127. package/scripts/discovery/repo-party.ts +80 -20
  128. package/scripts/discovery/session-owner-pin.ts +51 -0
  129. package/scripts/document/capped-print.ts +1 -1
  130. package/scripts/document/command-reference-line-budget.ts +42 -0
  131. package/scripts/document/document-byte-budget.ts +17 -7
  132. package/scripts/document/document-checks.ts +20 -0
  133. package/scripts/document/document-ratchet-check.ts +101 -0
  134. package/scripts/document/document-section.ts +1 -0
  135. package/scripts/document/entry-read-budget.ts +7 -3
  136. package/scripts/document/issue-citation-budget.ts +94 -0
  137. package/scripts/epic/epic-cli-argv.ts +2 -2
  138. package/scripts/epic/epic-fetch.ts +4 -2
  139. package/scripts/epic/epic-graph.ts +7 -2
  140. package/scripts/epic/epic-issue.ts +3 -1
  141. package/scripts/epic/epic-rank.ts +5 -0
  142. package/scripts/exports/unused-members-check.ts +62 -0
  143. package/scripts/exports/unused-members-cli.ts +6 -51
  144. package/scripts/followup/followup-ci-path.ts +67 -0
  145. package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
  146. package/scripts/followup/followup-coderabbit-wait.ts +13 -10
  147. package/scripts/followup/followup-failed-tests.ts +160 -0
  148. package/scripts/followup/followup-job-needs.ts +100 -0
  149. package/scripts/followup/followup-job-rerun.ts +303 -0
  150. package/scripts/followup/followup-main-move.ts +113 -0
  151. package/scripts/followup/git-followup-finish.ts +43 -11
  152. package/scripts/followup/git-followup-pending.ts +3 -3
  153. package/scripts/followup/git-followup-stages.ts +41 -11
  154. package/scripts/followup/git-pr-followup-checks.ts +55 -16
  155. package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
  156. package/scripts/followup/git-pr-followup.ts +62 -27
  157. package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
  158. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
  159. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
  160. package/scripts/gate/busy-average.ts +34 -2
  161. package/scripts/gate/check-related-pinned.ts +43 -0
  162. package/scripts/gate/check-related-reuse.ts +64 -0
  163. package/scripts/gate/check-related-section.ts +57 -0
  164. package/scripts/gate/check-related-unplanned.ts +34 -0
  165. package/scripts/gate/check-related.ts +265 -0
  166. package/scripts/gate/command-memory.ts +1 -1
  167. package/scripts/gate/core-admission.ts +24 -6
  168. package/scripts/gate/core-budget-timing.ts +20 -6
  169. package/scripts/gate/core-budget.ts +40 -11
  170. package/scripts/gate/core-wait-record.ts +3 -2
  171. package/scripts/gate/core-wait.ts +25 -2
  172. package/scripts/gate/core-weights.ts +15 -7
  173. package/scripts/gate/gate-labels.ts +46 -0
  174. package/scripts/gate/gate-ledger.ts +14 -6
  175. package/scripts/gate/gate-plan.ts +103 -58
  176. package/scripts/gate/gate-report.ts +9 -7
  177. package/scripts/gate/gate-run-steps.ts +26 -3
  178. package/scripts/gate/gate-skip.ts +45 -6
  179. package/scripts/gate/gate-static-group.ts +60 -0
  180. package/scripts/gate/gate-steps.ts +2 -1
  181. package/scripts/gate/gate-tree.ts +4 -2
  182. package/scripts/gate/hook-gate-reuse.ts +19 -2
  183. package/scripts/gate/metrics-totals.ts +59 -0
  184. package/scripts/gate/scoped-green.ts +68 -15
  185. package/scripts/gate/since-green.ts +14 -6
  186. package/scripts/gate/structural-checks.ts +96 -0
  187. package/scripts/gate/verification-gate.ts +51 -16
  188. package/scripts/gate/verification-inputs.ts +21 -6
  189. package/scripts/gate/verification-tail.ts +52 -0
  190. package/scripts/gh/gh-author-association.ts +23 -2
  191. package/scripts/gh/git-ai-review-scan.ts +1 -1
  192. package/scripts/gh/git-gh-api-path.ts +12 -3
  193. package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
  194. package/scripts/gh/git-gh-exec.ts +32 -2
  195. package/scripts/gh/git-gh-issue-read.ts +4 -2
  196. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  197. package/scripts/gh/git-gh-issue-write.ts +8 -2
  198. package/scripts/gh/git-gh-issue.ts +12 -5
  199. package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
  200. package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
  201. package/scripts/gh/git-gh-pr-read.ts +74 -13
  202. package/scripts/gh/git-gh-pr-rest.ts +3 -1
  203. package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
  204. package/scripts/gh/git-gh-pr-too-large.ts +20 -0
  205. package/scripts/gh/git-gh-pr.ts +21 -7
  206. package/scripts/gh/git-gh-retry.ts +141 -0
  207. package/scripts/gh/git-pr-ai-review.ts +15 -6
  208. package/scripts/gh/git-pr-checks-eval.ts +3 -0
  209. package/scripts/gh/git-pr-checks.ts +5 -5
  210. package/scripts/git/base-tree.ts +30 -0
  211. package/scripts/git/change-base.ts +4 -10
  212. package/scripts/git/changed-paths-sync.ts +63 -0
  213. package/scripts/git/changed-paths.ts +16 -1
  214. package/scripts/git/git-common-directory.ts +11 -1
  215. package/scripts/git/git-diff-reads.ts +66 -20
  216. package/scripts/git/git-error.ts +9 -2
  217. package/scripts/git/git-schemas.ts +13 -0
  218. package/scripts/git/git-worktree.ts +13 -0
  219. package/scripts/git/gone-branch.ts +4 -1
  220. package/scripts/git/main-merge-budget-conflict.ts +89 -0
  221. package/scripts/git/main-merge-guard.ts +55 -18
  222. package/scripts/git/main-merge-regenerate.ts +59 -25
  223. package/scripts/git/main-merge.ts +53 -5
  224. package/scripts/git/no-base-checkout.ts +127 -0
  225. package/scripts/git/stash/git-stash.ts +11 -2
  226. package/scripts/git/stash/stash-push-cli.ts +10 -0
  227. package/scripts/hooks/batch-guard.ts +40 -21
  228. package/scripts/hooks/edited-cspell.ts +37 -11
  229. package/scripts/hooks/format-edited-file.ts +22 -2
  230. package/scripts/hooks/lane-plan-guard.ts +50 -0
  231. package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
  232. package/scripts/hooks/pre-commit-lint.ts +14 -1
  233. package/scripts/hooks/pre-push-related-switch.ts +10 -0
  234. package/scripts/hooks/pre-push-unit.ts +76 -10
  235. package/scripts/hooks/pretool-guard.ts +2 -0
  236. package/scripts/hooks/reserved-run.ts +14 -1
  237. package/scripts/hooks/run-hook.sh +23 -8
  238. package/scripts/hooks/step-zero-notice.ts +6 -1
  239. package/scripts/hooks/stop-actions.ts +3 -15
  240. package/scripts/hooks/stop-guard.ts +7 -6
  241. package/scripts/init/distributed-paths.ts +1 -0
  242. package/scripts/init/hook-command-rewrite.ts +5 -1
  243. package/scripts/init/hook-launch.ts +4 -2
  244. package/scripts/issue/aggregation-split.ts +52 -0
  245. package/scripts/issue/defect-origin-line.ts +19 -1
  246. package/scripts/issue/defect-rate-cli.ts +5 -102
  247. package/scripts/issue/defect-report.ts +4 -4
  248. package/scripts/issue/effect-audit-cli.ts +178 -0
  249. package/scripts/issue/effect-audit.ts +185 -0
  250. package/scripts/issue/expected-effect-lint.ts +64 -9
  251. package/scripts/issue/git-next-issues.ts +5 -0
  252. package/scripts/issue/issue-body-or-comment.ts +38 -4
  253. package/scripts/issue/issue-cite-cli.ts +1 -3
  254. package/scripts/issue/issue-classification.ts +1 -0
  255. package/scripts/issue/issue-closing-pr.ts +8 -1
  256. package/scripts/issue/issue-evidence-exit.ts +4 -2
  257. package/scripts/issue/issue-file-cap.ts +58 -0
  258. package/scripts/issue/issue-file-cli.ts +111 -38
  259. package/scripts/issue/issue-file-deferral.ts +52 -0
  260. package/scripts/issue/issue-file-fold.ts +29 -10
  261. package/scripts/issue/issue-file-interrupt.ts +85 -0
  262. package/scripts/issue/issue-file-lane-hint.ts +30 -0
  263. package/scripts/issue/issue-file.ts +43 -6
  264. package/scripts/issue/issue-labels.ts +19 -2
  265. package/scripts/issue/issue-lint-cli.ts +27 -2
  266. package/scripts/issue/issue-measured-cli.ts +13 -35
  267. package/scripts/issue/issue-measured-close.ts +58 -0
  268. package/scripts/issue/issue-not-before-label.ts +2 -2
  269. package/scripts/issue/issue-not-before.ts +4 -1
  270. package/scripts/issue/issue-priority-cli.ts +60 -0
  271. package/scripts/issue/issue-priority-raise.ts +55 -0
  272. package/scripts/issue/issue-priority.ts +174 -43
  273. package/scripts/issue/issue-rank.ts +21 -4
  274. package/scripts/issue/issue-read-cli.ts +32 -6
  275. package/scripts/issue/issue-read.ts +37 -12
  276. package/scripts/issue/issue-sample-gate.ts +95 -74
  277. package/scripts/issue/issue-sample-prime.ts +127 -0
  278. package/scripts/issue/issue-sample-required.ts +57 -0
  279. package/scripts/issue/issue-sample-source.ts +12 -11
  280. package/scripts/issue/issue-search.ts +124 -0
  281. package/scripts/issue/issue-state.ts +13 -1
  282. package/scripts/issue/issue-template-section.ts +43 -0
  283. package/scripts/issue/issue-watch-diff.ts +31 -1
  284. package/scripts/issue/issue-watch-hook.ts +11 -2
  285. package/scripts/issue/issue-watch.ts +55 -12
  286. package/scripts/issue/markdown-section.ts +1 -0
  287. package/scripts/issue/measure-declaration.ts +121 -0
  288. package/scripts/issue/measure-rerun-cli.ts +7 -70
  289. package/scripts/issue/plan-consumers-section.ts +22 -1
  290. package/scripts/issue/reproduction-measure.ts +3 -1
  291. package/scripts/issue/size-estimate.ts +90 -0
  292. package/scripts/issue/trusted-measure.ts +84 -0
  293. package/scripts/josh/command-usage.ts +3 -2
  294. package/scripts/josh/durable-state-migrate.ts +105 -0
  295. package/scripts/josh/durable-state.ts +112 -0
  296. package/scripts/josh/josh-aliases.ts +95 -0
  297. package/scripts/josh/josh-command-map.ts +1 -82
  298. package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
  299. package/scripts/josh/josh-commands-ai.ts +5 -5
  300. package/scripts/josh/josh-commands-development.ts +59 -58
  301. package/scripts/josh/josh-commands-issue-write.ts +23 -4
  302. package/scripts/josh/josh-commands-issue.ts +32 -0
  303. package/scripts/josh/josh-commands-lane.ts +87 -4
  304. package/scripts/josh/josh-commands-lint.ts +16 -3
  305. package/scripts/josh/josh-commands-maintenance.ts +15 -0
  306. package/scripts/josh/josh-commands-review.ts +41 -6
  307. package/scripts/josh/josh-commands-ship.ts +56 -8
  308. package/scripts/josh/josh-commands-test-check.ts +80 -0
  309. package/scripts/josh/josh-commands-workflow.ts +1 -1
  310. package/scripts/josh/josh-environment-file.ts +43 -3
  311. package/scripts/josh/josh-in-process.ts +2 -2
  312. package/scripts/josh/josh-logic.ts +5 -3
  313. package/scripts/josh/josh-run.ts +19 -1
  314. package/scripts/josh/run-record-path.ts +15 -0
  315. package/scripts/josh/stamp-file.ts +11 -2
  316. package/scripts/lane/lane-adopt-cli.ts +98 -0
  317. package/scripts/lane/lane-adopt.ts +208 -0
  318. package/scripts/lane/lane-await.ts +5 -8
  319. package/scripts/lane/lane-baseline.ts +123 -0
  320. package/scripts/lane/lane-breakdown-cli.ts +64 -0
  321. package/scripts/lane/lane-breakdown-implement.ts +170 -0
  322. package/scripts/lane/lane-breakdown-laps.ts +107 -0
  323. package/scripts/lane/lane-breakdown-pinned.ts +76 -0
  324. package/scripts/lane/lane-breakdown-text.ts +145 -0
  325. package/scripts/lane/lane-breakdown.ts +239 -0
  326. package/scripts/lane/lane-check-stats.ts +29 -8
  327. package/scripts/lane/lane-child-invocation.ts +9 -2
  328. package/scripts/lane/lane-child-mark-keys.ts +12 -0
  329. package/scripts/lane/lane-child-marker.ts +20 -1
  330. package/scripts/lane/lane-ci-path-stats.ts +73 -0
  331. package/scripts/lane/lane-claim-stats.ts +84 -0
  332. package/scripts/lane/lane-close.ts +8 -3
  333. package/scripts/lane/lane-dispatch-log.ts +3 -3
  334. package/scripts/lane/lane-dispatch.ts +2 -2
  335. package/scripts/lane/lane-gap-stats.ts +7 -6
  336. package/scripts/lane/lane-guard-policy.ts +7 -1
  337. package/scripts/lane/lane-implement-stats.ts +207 -0
  338. package/scripts/lane/lane-implement-subagents.ts +90 -0
  339. package/scripts/lane/lane-investigation-switch.ts +16 -0
  340. package/scripts/lane/lane-ledger-kind.ts +31 -0
  341. package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
  342. package/scripts/lane/lane-ledger-schema.ts +119 -36
  343. package/scripts/lane/lane-ledger-write.ts +257 -0
  344. package/scripts/lane/lane-ledger.ts +31 -177
  345. package/scripts/lane/lane-load.ts +1 -1
  346. package/scripts/lane/lane-measure-cli.ts +176 -59
  347. package/scripts/lane/lane-net-read.ts +44 -0
  348. package/scripts/lane/lane-net-time.ts +212 -0
  349. package/scripts/lane/lane-open.ts +16 -7
  350. package/scripts/lane/lane-output.ts +2 -4
  351. package/scripts/lane/lane-overrun-prompt.ts +147 -0
  352. package/scripts/lane/lane-overrun.ts +274 -0
  353. package/scripts/lane/lane-paths.ts +24 -1
  354. package/scripts/lane/lane-pause-cli.ts +4 -1
  355. package/scripts/lane/lane-paused-spans.ts +97 -0
  356. package/scripts/lane/lane-plan-check.ts +207 -0
  357. package/scripts/lane/lane-plan-cli.ts +41 -0
  358. package/scripts/lane/lane-plan.ts +144 -0
  359. package/scripts/lane/lane-registry.ts +14 -1
  360. package/scripts/lane/lane-relaunch.ts +1 -1
  361. package/scripts/lane/lane-round-one-stats.ts +51 -0
  362. package/scripts/lane/lane-sampler.ts +23 -4
  363. package/scripts/lane/lane-session-action.ts +93 -18
  364. package/scripts/lane/lane-session-broker.ts +49 -21
  365. package/scripts/lane/lane-session-reread-main.ts +14 -0
  366. package/scripts/lane/lane-session-reread.ts +51 -0
  367. package/scripts/lane/lane-ship-lap-stats.ts +74 -0
  368. package/scripts/lane/lane-sleep.ts +58 -0
  369. package/scripts/lane/lane-spent.ts +1 -1
  370. package/scripts/lane/lane-split-stats.ts +28 -0
  371. package/scripts/lane/lane-stage-stats.ts +13 -1
  372. package/scripts/lane/lane-state.ts +41 -9
  373. package/scripts/lane/lane-stats-tables.ts +58 -0
  374. package/scripts/lane/lane-stats.ts +21 -5
  375. package/scripts/lane/lane-stop-stats.ts +51 -19
  376. package/scripts/lane/lane-tail-stats.ts +32 -0
  377. package/scripts/lane/lane-transcript-calls.ts +67 -10
  378. package/scripts/lane/lane-transcript-stats.ts +44 -12
  379. package/scripts/lane/lane-value-cell.ts +72 -0
  380. package/scripts/lane/lane-value.ts +118 -0
  381. package/scripts/lane/lane-withdraw-closed.ts +124 -0
  382. package/scripts/lane/lane-withdraw.ts +108 -31
  383. package/scripts/lane/lane-yield.ts +85 -0
  384. package/scripts/lane/openai-lane-ship.ts +12 -11
  385. package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
  386. package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
  387. package/scripts/lane/openai-lane-supervisor.ts +5 -4
  388. package/scripts/lib/poll.ts +38 -9
  389. package/scripts/lib/settled-together.ts +10 -0
  390. package/scripts/lib/shell-quote.ts +21 -0
  391. package/scripts/lib/token-shape.ts +111 -0
  392. package/scripts/lint/eslint-daemon-sources.ts +79 -0
  393. package/scripts/lint/eslint-daemon.ts +45 -6
  394. package/scripts/lint/eslint-warm-cli.ts +7 -0
  395. package/scripts/lint/eslint-warm.ts +66 -0
  396. package/scripts/lint/import-graph.ts +141 -11
  397. package/scripts/lint/lint-gate-scope.ts +2 -2
  398. package/scripts/lint/lint-parallel.ts +54 -6
  399. package/scripts/lint/lint-related.ts +154 -33
  400. package/scripts/lint/rename-cli.ts +179 -0
  401. package/scripts/lint/rename-identifier.ts +192 -0
  402. package/scripts/lint/type-only-imports.ts +58 -0
  403. package/scripts/notify/telegram-notify.ts +12 -3
  404. package/scripts/observations/observation-record.ts +42 -16
  405. package/scripts/observations/observations-flush.ts +32 -9
  406. package/scripts/overrides/overrides-check.ts +9 -1
  407. package/scripts/release/release-approval-prune.ts +7 -1
  408. package/scripts/repo/required-checks-logic.ts +4 -1
  409. package/scripts/repo/ruleset-check.ts +4 -3
  410. package/scripts/report/markdown-shape.ts +28 -0
  411. package/scripts/report/report-format-reference.ts +15 -1
  412. package/scripts/report/report-labels.ts +12 -1
  413. package/scripts/report/report-lint-child-summary.ts +94 -0
  414. package/scripts/report/report-lint-cli.ts +26 -7
  415. package/scripts/report/report-lint.ts +2 -20
  416. package/scripts/review/prose-stale-cli.ts +88 -0
  417. package/scripts/review/prose-stale-scan.ts +152 -0
  418. package/scripts/review/review-attest.ts +73 -23
  419. package/scripts/review/review-brief-cli.ts +68 -19
  420. package/scripts/review/review-brief-paths.ts +22 -0
  421. package/scripts/review/review-brief.ts +14 -7
  422. package/scripts/review/review-comment-carry.ts +47 -0
  423. package/scripts/review/review-comment-round.ts +85 -22
  424. package/scripts/review/review-finding-ledger.ts +109 -7
  425. package/scripts/review/review-finding-text.ts +43 -0
  426. package/scripts/review/review-interaction.ts +11 -0
  427. package/scripts/review/review-lane-handover.ts +68 -0
  428. package/scripts/review/review-lint-cli.ts +57 -0
  429. package/scripts/review/review-lint-findings.ts +92 -0
  430. package/scripts/review/review-lint-sections.ts +80 -0
  431. package/scripts/review/review-lint.ts +221 -0
  432. package/scripts/review/review-prior-findings.ts +102 -0
  433. package/scripts/review/review-readers-scan.ts +4 -2
  434. package/scripts/review/review-record-cli.ts +177 -54
  435. package/scripts/review/review-record-code-round.ts +55 -0
  436. package/scripts/review/review-record-comment.ts +60 -0
  437. package/scripts/review/review-record-interaction.ts +35 -0
  438. package/scripts/review/review-record-pinned.ts +108 -0
  439. package/scripts/review/review-record.ts +25 -1
  440. package/scripts/review/review-reviewed-tree.ts +307 -0
  441. package/scripts/review/review-round-one-closure.ts +62 -0
  442. package/scripts/review/review-round-reuse.ts +106 -0
  443. package/scripts/review/review-round2-cli.ts +91 -29
  444. package/scripts/review/review-round2-skip-comment.ts +55 -0
  445. package/scripts/review/review-round2.ts +24 -8
  446. package/scripts/review/review-stamps.ts +7 -3
  447. package/scripts/review/review-stop-log.ts +20 -6
  448. package/scripts/review/review-stops-cli.ts +2 -1
  449. package/scripts/review/review-submit-cli.ts +101 -0
  450. package/scripts/rules/agent-model.ts +1 -1
  451. package/scripts/rules/bash-triggers.ts +3 -3
  452. package/scripts/rules/concise-reply.ts +88 -0
  453. package/scripts/rules/credential-body.ts +77 -0
  454. package/scripts/rules/decision-oracle.ts +1 -1
  455. package/scripts/rules/delivered-rules.ts +36 -17
  456. package/scripts/rules/destructive-command.ts +3 -11
  457. package/scripts/rules/direct-issue-comment.ts +34 -12
  458. package/scripts/rules/direct-issue-read.ts +191 -0
  459. package/scripts/rules/direct-pr-create.ts +4 -24
  460. package/scripts/rules/early-heartbeat.ts +2 -0
  461. package/scripts/rules/edit-tool.ts +27 -0
  462. package/scripts/rules/file-edit-rows.ts +17 -0
  463. package/scripts/rules/file-rewrite.ts +146 -0
  464. package/scripts/rules/firing-point.ts +3 -2
  465. package/scripts/rules/gh-api.ts +128 -7
  466. package/scripts/rules/gh-repo-selector.ts +84 -0
  467. package/scripts/rules/git-force.ts +2 -2
  468. package/scripts/rules/guard-remedy.ts +14 -5
  469. package/scripts/rules/implementation-cut.ts +61 -28
  470. package/scripts/rules/investigator-foreground.ts +231 -0
  471. package/scripts/rules/issue-citation.ts +15 -60
  472. package/scripts/rules/issue-comments.ts +9 -4
  473. package/scripts/rules/josh-git-bare.ts +1 -1
  474. package/scripts/rules/lane-background.ts +41 -13
  475. package/scripts/rules/lane-direct-tool.ts +107 -9
  476. package/scripts/rules/lane-interactive-ask.ts +4 -1
  477. package/scripts/rules/lane-park.ts +4 -2
  478. package/scripts/rules/lane-spilled-read.ts +90 -0
  479. package/scripts/rules/lane-split-park.ts +1 -1
  480. package/scripts/rules/lane-switch-main.ts +4 -4
  481. package/scripts/rules/last-prompt.ts +20 -47
  482. package/scripts/rules/oracle-consulted.ts +1 -1
  483. package/scripts/rules/permission-guards.ts +5 -1
  484. package/scripts/rules/piped-verification-shapes.ts +110 -0
  485. package/scripts/rules/piped-verification.ts +67 -17
  486. package/scripts/rules/poll-loop.ts +72 -9
  487. package/scripts/rules/pr-merge.ts +5 -5
  488. package/scripts/rules/pre-gate-cut.ts +3 -3
  489. package/scripts/rules/protected-files.ts +88 -24
  490. package/scripts/rules/reply-language.ts +3 -17
  491. package/scripts/rules/reply-prose.ts +34 -0
  492. package/scripts/rules/repo-admin.ts +132 -0
  493. package/scripts/rules/reviewer-foreground.ts +88 -0
  494. package/scripts/rules/round-one-check-related.ts +120 -0
  495. package/scripts/rules/rule-body-guard.ts +5 -15
  496. package/scripts/rules/rule-value-cache.ts +53 -5
  497. package/scripts/rules/rule-value-candidates.ts +84 -0
  498. package/scripts/rules/rule-value-cli.ts +59 -11
  499. package/scripts/rules/rule-value.ts +17 -4
  500. package/scripts/rules/run-tail-rule.ts +1 -1
  501. package/scripts/rules/sed-in-place.ts +196 -0
  502. package/scripts/rules/shell-segments.ts +10 -2
  503. package/scripts/rules/stop-rule-rows.ts +1 -13
  504. package/scripts/rules/stop-rules.ts +33 -58
  505. package/scripts/rules/test-declared-commit.ts +1 -1
  506. package/scripts/rules/third-party-write.ts +110 -23
  507. package/scripts/rules/timed-rerun.ts +1 -1
  508. package/scripts/rules/turn-filings.ts +90 -0
  509. package/scripts/rules/worktree-guard.ts +22 -7
  510. package/scripts/run/board/run-board-ending-reason.ts +6 -2
  511. package/scripts/run/board/run-board-fit.ts +24 -1
  512. package/scripts/run/board/run-board-found.ts +134 -0
  513. package/scripts/run/board/run-board-header.ts +8 -0
  514. package/scripts/run/board/run-board-labels.ts +11 -1
  515. package/scripts/run/board/run-board-lane-time.ts +1 -0
  516. package/scripts/run/board/run-board-link.ts +15 -2
  517. package/scripts/run/board/run-board-notes.ts +7 -1
  518. package/scripts/run/board/run-board-operation.ts +4 -2
  519. package/scripts/run/board/run-board-phase.ts +2 -5
  520. package/scripts/run/board/run-board-plan.ts +1 -1
  521. package/scripts/run/board/run-board-read.ts +46 -1
  522. package/scripts/run/board/run-board-reason.ts +20 -8
  523. package/scripts/run/board/run-board-render-checks.ts +3 -2
  524. package/scripts/run/board/run-board-render-drawn.ts +11 -0
  525. package/scripts/run/board/run-board-render-found.ts +157 -0
  526. package/scripts/run/board/run-board-render-lead.ts +9 -0
  527. package/scripts/run/board/run-board-render-legend.ts +19 -0
  528. package/scripts/run/board/run-board-render-tail.ts +1 -1
  529. package/scripts/run/board/run-board-render.ts +82 -18
  530. package/scripts/run/board/run-board-spin.ts +1 -3
  531. package/scripts/run/board/run-board-status.ts +2 -0
  532. package/scripts/run/board/run-board-tick.ts +39 -10
  533. package/scripts/run/carry/run-carry-ended.ts +3 -2
  534. package/scripts/run/carry/run-carry.ts +3 -2
  535. package/scripts/run/cut/run-cut-cli.ts +33 -133
  536. package/scripts/run/cut/run-cut-digest.ts +152 -0
  537. package/scripts/run/cut/run-cut-handoff.ts +52 -8
  538. package/scripts/run/cut/run-cut-report.ts +24 -3
  539. package/scripts/run/cut/run-cut-resume.ts +129 -0
  540. package/scripts/run/cut/run-cut-review.ts +105 -0
  541. package/scripts/run/cut/run-cut.ts +18 -7
  542. package/scripts/run/detached-launch.ts +1 -1
  543. package/scripts/run/entry/run-entry-cli.ts +88 -19
  544. package/scripts/run/entry/run-entry-spill.ts +83 -0
  545. package/scripts/run/entry/run-entry-stop.ts +5 -1
  546. package/scripts/run/entry/run-entry-trust.ts +91 -0
  547. package/scripts/run/event/run-event-baseline.ts +56 -0
  548. package/scripts/run/event/run-event-filed.ts +9 -3
  549. package/scripts/run/event/run-event-overrun.ts +197 -0
  550. package/scripts/run/event/run-event-render.ts +4 -0
  551. package/scripts/run/event/run-event-stream.ts +25 -3
  552. package/scripts/run/hold/run-stop-notify.ts +3 -8
  553. package/scripts/run/merge/run-merge-cap.ts +57 -0
  554. package/scripts/run/merge/run-merge-cli.ts +66 -23
  555. package/scripts/run/merge/run-merge-outcome.ts +2 -0
  556. package/scripts/run/merge/run-merge-ship.ts +13 -3
  557. package/scripts/run/merge/run-merge-signals.ts +40 -23
  558. package/scripts/run/merge/run-merge-steps.ts +56 -36
  559. package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
  560. package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
  561. package/scripts/run/merge/run-merge.ts +41 -14
  562. package/scripts/run/review/run-review-cli.ts +306 -20
  563. package/scripts/run/review/run-review-early-push.ts +121 -0
  564. package/scripts/run/review/run-review-join-read.ts +94 -0
  565. package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
  566. package/scripts/run/review/run-review-pinned-join.ts +216 -0
  567. package/scripts/run/review/run-review-pinned-steps.ts +383 -0
  568. package/scripts/run/review/run-review-pinned-tree.ts +37 -0
  569. package/scripts/run/review/run-review-pinned.ts +53 -0
  570. package/scripts/run/review/run-review-steps.ts +70 -34
  571. package/scripts/run/review/run-review.ts +26 -6
  572. package/scripts/run/run-parent-cut-hook.ts +3 -4
  573. package/scripts/run/run-prep.ts +1 -0
  574. package/scripts/run/run-stage-read.ts +2 -6
  575. package/scripts/run/run-step-cli.ts +17 -5
  576. package/scripts/run/run-step-lane-plan.ts +27 -0
  577. package/scripts/run/run-step-section.ts +26 -7
  578. package/scripts/run/run-step.ts +5 -2
  579. package/scripts/run/ship/run-ship-args.ts +19 -0
  580. package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
  581. package/scripts/run/ship/run-ship-cli.ts +131 -39
  582. package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
  583. package/scripts/run/ship/run-ship-detach.ts +10 -4
  584. package/scripts/run/ship/run-ship-handover.ts +106 -0
  585. package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
  586. package/scripts/run/ship/run-ship-lap.ts +63 -0
  587. package/scripts/run/ship/run-ship-next.ts +27 -26
  588. package/scripts/run/ship/run-ship-overlap.ts +8 -3
  589. package/scripts/run/ship/run-ship-place.ts +9 -2
  590. package/scripts/run/ship/run-ship-planned.ts +92 -0
  591. package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
  592. package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
  593. package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
  594. package/scripts/run/ship/run-ship-preflight.ts +16 -4
  595. package/scripts/run/ship/run-ship-probe.ts +23 -9
  596. package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
  597. package/scripts/run/ship/run-ship-relaunch.ts +51 -0
  598. package/scripts/run/ship/run-ship-return.ts +103 -29
  599. package/scripts/run/ship/run-ship-review-reason.ts +24 -0
  600. package/scripts/run/ship/run-ship-review-steps.ts +230 -59
  601. package/scripts/run/ship/run-ship-review.ts +171 -51
  602. package/scripts/run/ship/run-ship-reviewer.ts +138 -42
  603. package/scripts/run/ship/run-ship-scoped.ts +39 -10
  604. package/scripts/run/ship/run-ship-stage.ts +49 -13
  605. package/scripts/run/ship/run-ship-steps.ts +24 -4
  606. package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
  607. package/scripts/run/ship/run-ship-stop-context.ts +50 -13
  608. package/scripts/run/ship/run-ship-sync.ts +171 -30
  609. package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
  610. package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
  611. package/scripts/run/ship/run-ship-waiter.ts +114 -0
  612. package/scripts/run/ship/run-ship.ts +16 -2
  613. package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
  614. package/scripts/run/wake/run-wake-cli.ts +6 -113
  615. package/scripts/run/wake/run-wake-code.ts +53 -11
  616. package/scripts/run/wake/run-wake-driver.ts +5 -3
  617. package/scripts/run/wake/run-wake-finish.ts +137 -0
  618. package/scripts/run/wake/run-wake-loop.ts +5 -3
  619. package/scripts/run/wake/run-wake-ports.ts +11 -3
  620. package/scripts/run/wake/run-wake-session.ts +2 -0
  621. package/scripts/run/wake/run-wake.ts +3 -2
  622. package/scripts/safe-chain/ci-installer-pin.ts +49 -8
  623. package/scripts/safe-chain/project-config.ts +3 -2
  624. package/scripts/security/secretlint-scan.ts +42 -9
  625. package/scripts/security/security-audit-provision-main.ts +9 -1
  626. package/scripts/security/security-audit-provision.ts +1 -1
  627. package/scripts/security/security-audit.ts +21 -1
  628. package/scripts/security/sonar-file.ts +10 -5
  629. package/scripts/split/split-assess.ts +12 -3
  630. package/scripts/split/split-early-notice.ts +116 -0
  631. package/scripts/split/split-measure.ts +20 -2
  632. package/scripts/split/split-resolution.ts +107 -0
  633. package/scripts/test/direct-importers.ts +185 -0
  634. package/scripts/test/pure-files-entries.ts +83 -0
  635. package/scripts/test/related-heavy-tests.ts +81 -0
  636. package/scripts/test/test-no-base.ts +88 -0
  637. package/scripts/test/test-pinned-cache-logic.ts +363 -0
  638. package/scripts/test/test-pinned-cache.ts +337 -0
  639. package/scripts/test/test-pinned-emit.ts +49 -0
  640. package/scripts/test/test-pinned-functions.ts +116 -0
  641. package/scripts/test/test-pinned-ledger.ts +80 -0
  642. package/scripts/test/test-pinned-logic.ts +208 -20
  643. package/scripts/test/test-pinned-main.ts +84 -18
  644. package/scripts/test/test-pinned-pool.ts +73 -0
  645. package/scripts/test/test-pinned-probe.ts +254 -0
  646. package/scripts/test/test-pinned-prune-logic.ts +118 -0
  647. package/scripts/test/test-pinned-reach.ts +194 -0
  648. package/scripts/test/test-pinned-record.ts +196 -0
  649. package/scripts/test/test-pinned-rewire.ts +148 -0
  650. package/scripts/test/test-pinned-spawn.ts +81 -0
  651. package/scripts/test/test-pinned-stage.ts +70 -0
  652. package/scripts/test/test-pinned.ts +258 -47
  653. package/scripts/test/test-reach-cli.ts +61 -0
  654. package/scripts/test/test-read-map.ts +35 -11
  655. package/scripts/test/test-read-recording.ts +17 -5
  656. package/scripts/test/test-read-trace.ts +26 -8
  657. package/scripts/test/test-red-logic.ts +46 -7
  658. package/scripts/test/test-red.ts +34 -14
  659. package/scripts/test/test-related-claim.ts +37 -0
  660. package/scripts/test/test-related-main.ts +4 -1
  661. package/scripts/test/test-related-moved.ts +91 -0
  662. package/scripts/test/test-related-record.ts +43 -23
  663. package/scripts/test/test-related-scope.ts +99 -34
  664. package/scripts/test/test-related-synced.ts +135 -0
  665. package/scripts/test/test-related-touched.ts +84 -0
  666. package/scripts/test/test-related.ts +167 -83
  667. package/scripts/test/test-unit-guard.ts +28 -5
  668. package/scripts/test/unit-durations.ts +41 -0
  669. package/scripts/test/unit-worker-share.ts +5 -4
  670. package/scripts/test/vitest-cache-directory.ts +19 -0
  671. package/scripts/test/vitest-cli-excludes.ts +14 -0
  672. package/scripts/test/vitest-failure-summary.ts +111 -0
  673. package/scripts/test/whole-suite-files.ts +12 -1
  674. package/scripts/time-runtime/time-batch-guard.ts +42 -4
  675. package/scripts/time-runtime/time-bundle-call.ts +1 -1
  676. package/scripts/time-runtime/time-bundles.ts +36 -5
  677. package/scripts/time-runtime/time-markers.ts +2 -2
  678. package/scripts/time-runtime/time-search-call.ts +38 -0
  679. package/scripts/time-runtime/time-shell.ts +1 -1
  680. package/scripts/time-runtime/time-transcript-line.ts +17 -5
  681. package/scripts/version/development-engines-verdict.ts +124 -0
  682. package/scripts/version/latest-corepack.ts +2 -0
  683. package/templates/gitignore +2 -0
  684. package/templates/workflows/ci.yml +41 -0
  685. package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
  686. package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
  687. package/dist/hooks/chunk-TUIFS5E7.js +0 -72
  688. package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
  689. package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
  690. package/scripts/rules/filing-cap.ts +0 -139
@@ -0,0 +1,307 @@
1
+ import { gate_tree, type GateTree } from '#scripts/gate/gate-tree'
2
+ import { moved_paths, type MovedParts } from '#scripts/gate/moved-paths'
3
+ import { PROJECT_ROOT } from '#scripts/init/init-paths'
4
+ import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
5
+ import { stamp_file } from '#scripts/josh/stamp-file'
6
+ import { release_approval_prune } from '#scripts/release/release-approval-prune'
7
+ import { test_declared_logic } from '#scripts/test/test-declared-logic'
8
+ import { review_attest } from './review-attest'
9
+ import { review_checkout, type ReviewCheckout } from './review-checkout'
10
+ import { review_round_one_blobs } from './review-round-one-blobs'
11
+ import { review_round2 } from './review-round2'
12
+ import { review_stamps } from './review-stamps'
13
+
14
+ // The tree a recorded `/code-review` round stands for: the changed files as they were when
15
+ // `review:record` wrote the round, its fixes included (joshuafolkken/kit#4620). A lane reviews before
16
+ // `josh ship`, so the ship's round 1 asks this record whether the tree moved since: unmoved, the
17
+ // recorded round is the ship's; moved only where round 2 reads, it is still the ship's and round 2
18
+ // reads the delta (joshuafolkken/kit#4878); else round 1 reads the delta from here.
19
+ //
20
+ // **Taken at the record, not the brief**. A round that fixed findings approved its own edits, so the
21
+ // briefed tree is not what its verdict covers: a file reverted to the briefed bytes would drop out of a
22
+ // delta measured from there with its finding back in it.
23
+
24
+ const LINE_SUFFIX = /:\d+(?:-\d+)?$/u
25
+ // No file's digest: a path recorded with it never matches the tree, whether the change holds it or not.
26
+ const UNSETTLED_DIGEST = 'unsettled'
27
+
28
+ // **A file a recorded High cites is recorded as unsettled**: a High is never fixed in place, so the
29
+ // round's verdict does not stand for that file. Its digest matches no tree, so it always reads as moved
30
+ // — the tree is never held, and round 2 reads its fix where its delta holds it (joshuafolkken/kit#4941);
31
+ // anywhere else round 1 reads it again rather than passing over the finding it still holds. A file
32
+ // outside the change (a reader the change broke) is recorded too: left out, it would be absent on both
33
+ // sides and read as unmoved.
34
+ function settled_files(
35
+ files: Record<string, string>,
36
+ unsettled: ReadonlyArray<string>,
37
+ ): Record<string, string> {
38
+ const cited = unsettled.map((file): [string, string] => [
39
+ file.replace(LINE_SUFFIX, ''),
40
+ UNSETTLED_DIGEST,
41
+ ])
42
+
43
+ return { ...files, ...Object.fromEntries(cited) }
44
+ }
45
+
46
+ // Swallowed: no record re-runs the ship's round 1 over the whole change — wider, never narrower. The
47
+ // blobs let a sync's change to a covered document carry onto the new base (`carried_paths`).
48
+ async function record(unsettled: ReadonlyArray<string> = []): Promise<void> {
49
+ try {
50
+ const { files, base } = await gate_tree.read_gate_tree()
51
+ const blobs = review_round_one_blobs.capture(Object.keys(files))
52
+
53
+ review_stamps.reviewed_stamp.write(settled_files(files, unsettled), undefined, base, blobs)
54
+ } catch {
55
+ /* no record widens the next round rather than narrowing it */
56
+ }
57
+ }
58
+
59
+ // An empty tree or an unresolved base is git not answering, never "nothing moved".
60
+ //
61
+ // **A base the ship's sync moved is unmoved for every path the default branch left alone**
62
+ // (joshuafolkken/kit#4829): with every recorded digest matching, only a synced path the round covers
63
+ // changes what it read — unless round 2 would not execute it and main alone moved it
64
+ // (joshuafolkken/kit#4984). Main's other paths are the scoped pair's, the gate's and CI's to check.
65
+ //
66
+ // **A `josh metrics` approval the branch rewrote is no move** (joshuafolkken/kit#4788): the ship's
67
+ // pre-detach re-record writes it after `review:record`, and it is data `josh metrics` derives and the
68
+ // gate's own metrics step checks, never what the round reviewed. An approval a recorded High cites
69
+ // stays unsettled, so it still reads as moved.
70
+ function is_rerecorded_approval(stamp: FileMapStamp, path: string): boolean {
71
+ return release_approval_prune.is_approval(path) && stamp.files[path] !== UNSETTLED_DIGEST
72
+ }
73
+
74
+ // **A test added or edited since the record is no move** (joshuafolkken/kit#4874): `chain-rule.md`
75
+ // step 0 pins each `test:pinned` unpinned place after `review:record`, and a test changes no runtime
76
+ // the round's verdict covers; round 2 still reads the test delta (`review-round2.ts` keeps tests live).
77
+ // A test removed since, or one a recorded High cites, still reads as moved.
78
+ function is_pinning_test(stamp: FileMapStamp, tree: GateTree, path: string): boolean {
79
+ const is_settled = Object.hasOwn(tree.files, path) && stamp.files[path] !== UNSETTLED_DIGEST
80
+
81
+ return is_settled && test_declared_logic.is_test_file(path)
82
+ }
83
+
84
+ function is_no_move(stamp: FileMapStamp, tree: GateTree, path: string): boolean {
85
+ return is_rerecorded_approval(stamp, path) || is_pinning_test(stamp, tree, path)
86
+ }
87
+
88
+ // How the tree stands against the record: `held` — nothing the round read moved; `delta` — every move
89
+ // is one round 2 reads (joshuafolkken/kit#4878); `unsettled` — any other move, or git did not answer.
90
+ const MOVEMENT = { DELTA: 'delta', HELD: 'held', UNSETTLED: 'unsettled' } as const
91
+
92
+ type Movement = (typeof MOVEMENT)[keyof typeof MOVEMENT]
93
+
94
+ // The movement, and on `unsettled` the condition that decided it, with the path that met it where one
95
+ // did — printed by `review:record --check` so a lane reads why the ship reviews round 1 again
96
+ // (joshuafolkken/kit#4941).
97
+ interface Standing {
98
+ movement: Movement
99
+ cause?: string
100
+ }
101
+
102
+ const CAUSE = {
103
+ NO_RECORD: 'no reviewed tree was recorded',
104
+ SKIPPED:
105
+ 'a move round 2 would not read — documentation, a prompt, an inert path, a revert to the briefed bytes, or a file outside the change',
106
+ SYNCED: 'the default branch changed a file the round covers',
107
+ UNFIXED:
108
+ "a file a recorded High cites still holds its round-1 brief bytes, so no fix is in round 2's delta",
109
+ UNATTESTED: 'the round was not attested on this branch',
110
+ UNREAD: 'git did not answer',
111
+ } as const
112
+
113
+ function unsettled_by(cause: string, path?: string): Standing {
114
+ return { movement: MOVEMENT.UNSETTLED, cause: path === undefined ? cause : `${cause}: ${path}` }
115
+ }
116
+
117
+ // **A covered file only the default branch moved is no move where round 2 would not execute it**
118
+ // (joshuafolkken/kit#4984): documentation, a prompt or an inert path — a generated usage heading main
119
+ // rewrote beside the lane's own. Its recorded blob carried onto the new base reproduces the file now
120
+ // only where main's change is all that moved it (`review-round-one-blobs.ts`); a lane edit since the
121
+ // record, a carry git cannot make, or a path a recorded High cites stays moved.
122
+ function is_carry_candidate(stamp: FileMapStamp, path: string): boolean {
123
+ const is_settled = Object.hasOwn(stamp.files, path) && stamp.files[path] !== UNSETTLED_DIGEST
124
+
125
+ return is_settled && review_round2.is_unexecuted(path, [])
126
+ }
127
+
128
+ function carried_paths(stamp: FileMapStamp, tree: GateTree, parts: MovedParts): Array<string> {
129
+ const candidates = parts.synced.filter((path) => is_carry_candidate(stamp, path))
130
+ const { blobs, base: from } = stamp
131
+ const delta =
132
+ candidates.length === 0
133
+ ? undefined
134
+ : review_round_one_blobs.carried_delta({ blobs, from, to: tree.base, paths: candidates })
135
+
136
+ return delta === undefined ? [] : candidates.filter((path) => !delta.includes(path))
137
+ }
138
+
139
+ function moved_files(stamp: FileMapStamp, tree: GateTree, parts: MovedParts): Array<string> {
140
+ return parts.changed.filter((path) => !is_no_move(stamp, tree, path))
141
+ }
142
+
143
+ // **Round 2 reads a move only where its own delta holds it**: it diffs from the round-1 brief's stamp
144
+ // (`briefed`) and passes over documentation, prompts and inert paths (`review-round2.ts`), so a file
145
+ // reverted to the briefed bytes, or one round 2 skips, would ship unread. A path a recorded High cites
146
+ // is read there too — its fix is the delta round 2 verifies, and its brief names the file as a High's
147
+ // fix (`review-brief.ts`). A default-branch change to a covered file is outside round 2's carried delta.
148
+ function is_read_by_round_two(
149
+ path: string,
150
+ tree: GateTree,
151
+ briefed: FileMapStamp | undefined,
152
+ ): boolean {
153
+ if (review_round2.is_unexecuted(path, [])) return false
154
+
155
+ return briefed?.files[path] !== tree.files[path]
156
+ }
157
+
158
+ // An unread High-cited file round 2 would execute is one whose fix has not landed, not a revert.
159
+ function unread_cause(stamp: FileMapStamp, path: string): string {
160
+ const is_cited = stamp.files[path] === UNSETTLED_DIGEST
161
+
162
+ return is_cited && !review_round2.is_unexecuted(path, []) ? CAUSE.UNFIXED : CAUSE.SKIPPED
163
+ }
164
+
165
+ function moved_standing(
166
+ stamp: FileMapStamp,
167
+ moved: ReadonlyArray<string>,
168
+ tree: GateTree,
169
+ briefed: FileMapStamp | undefined,
170
+ ): Standing {
171
+ if (moved.length === 0) return { movement: MOVEMENT.HELD }
172
+
173
+ const unread = moved.find((path) => !is_read_by_round_two(path, tree, briefed))
174
+
175
+ if (unread === undefined) return { movement: MOVEMENT.DELTA }
176
+
177
+ return unsettled_by(unread_cause(stamp, unread), unread)
178
+ }
179
+
180
+ async function standing(
181
+ stamp: FileMapStamp,
182
+ tree: GateTree,
183
+ briefed?: FileMapStamp,
184
+ ): Promise<Standing> {
185
+ const parts = await moved_paths.moved_parts(stamp, tree)
186
+
187
+ if (parts === undefined) return unsettled_by(CAUSE.UNREAD)
188
+
189
+ const carried = carried_paths(stamp, tree, parts)
190
+ const synced = parts.synced.find(
191
+ (path) => Object.hasOwn(stamp.files, path) && !carried.includes(path),
192
+ )
193
+
194
+ if (synced !== undefined) return unsettled_by(CAUSE.SYNCED, synced)
195
+
196
+ const moved = moved_files(stamp, tree, parts).filter((path) => !carried.includes(path))
197
+
198
+ return moved_standing(stamp, moved, tree, briefed)
199
+ }
200
+
201
+ async function movement(
202
+ stamp: FileMapStamp,
203
+ tree: GateTree,
204
+ briefed?: FileMapStamp,
205
+ ): Promise<Movement> {
206
+ const answer = await standing(stamp, tree, briefed)
207
+
208
+ return answer.movement
209
+ }
210
+
211
+ async function is_unmoved(stamp: FileMapStamp, tree: GateTree): Promise<boolean> {
212
+ return (await movement(stamp, tree)) === MOVEMENT.HELD
213
+ }
214
+
215
+ // **Only a review attested on this branch is reused**, as `resume_point` narrows from no other: the
216
+ // reuse skips the ship's own `review:attest --check`, so a round recorded over a review that read
217
+ // another checkout, or never finished, would otherwise ship unreviewed.
218
+ async function standing_here(): Promise<Standing> {
219
+ const stamp = review_stamps.reviewed_stamp.read()
220
+
221
+ if (stamp === undefined) return unsettled_by(CAUSE.NO_RECORD)
222
+
223
+ const checkout = await review_checkout.read_checkout()
224
+
225
+ if (!review_attest.is_attested_on(checkout, checkout.root)) return unsettled_by(CAUSE.UNATTESTED)
226
+
227
+ const tree = await gate_tree.read_gate_tree()
228
+
229
+ return await standing(stamp, tree, review_stamps.round_one_stamp.read())
230
+ }
231
+
232
+ // The files a recorded High cites — the ones the round-2 brief names as a High's fix to read whole.
233
+ function high_cited(): ReadonlyArray<string> {
234
+ const files = review_stamps.reviewed_stamp.read()?.files ?? {}
235
+
236
+ return Object.keys(files).filter((path) => files[path] === UNSETTLED_DIGEST)
237
+ }
238
+
239
+ // The record a resumed brief narrows from: written after the brief the pointer names, whose review was
240
+ // attested on this branch. Asked before the next brief mints its pointer, which this record then
241
+ // predates — so it narrows one round, never two.
242
+ function resume_point(checkout: ReviewCheckout): FileMapStamp | undefined {
243
+ const stamp = review_stamps.reviewed_stamp.read()
244
+
245
+ if (stamp === undefined || !review_attest.is_briefed_since(stamp.taken_at, checkout.root)) {
246
+ return undefined
247
+ }
248
+
249
+ return review_attest.is_attested_on(checkout, checkout.root) ? stamp : undefined
250
+ }
251
+
252
+ // **A ship's round 1 that stopped on a finding stays its round 1 however the tree moves since**: the
253
+ // tree moves by the fix, and the fix delta is round 2's to verify (joshuafolkken/kit#2964). Reviewed
254
+ // as round 1 again, every stop would cost one more review and one more chance to stop. The marker
255
+ // names the issue, so a checkout that ships another issue next never reuses this one's round.
256
+ const STOPPED_PREFIX = 'josh-review-round1-stopped-'
257
+
258
+ function stopped_path(): string {
259
+ return stamp_file.stamp_path(STOPPED_PREFIX, PROJECT_ROOT)
260
+ }
261
+
262
+ // Written when the ship's round 1 stops on a finding, removed when it ends any other way. A write the
263
+ // temp directory refuses is swallowed: no marker re-runs round 1 — wider, never narrower.
264
+ function mark_stopped(issue: number, is_stopped: boolean): void {
265
+ if (!is_stopped) {
266
+ stamp_file.remove_stamp(stopped_path())
267
+
268
+ return
269
+ }
270
+
271
+ try {
272
+ stamp_file.write_text_stamp(stopped_path(), String(issue))
273
+ } catch {
274
+ /* no marker reviews round 1 again rather than passing over it */
275
+ }
276
+ }
277
+
278
+ function is_stopped_on(issue: number): boolean {
279
+ return stamp_file.read_stamp_text(stopped_path()) === String(issue)
280
+ }
281
+
282
+ // The ship drops both records when the issue changed after the recorded round's brief: that round never
283
+ // read the new specification, so the next one reviews the whole change.
284
+ function discard(): void {
285
+ try {
286
+ review_stamps.reviewed_stamp.remove()
287
+ stamp_file.remove_stamp(stopped_path())
288
+ } catch {
289
+ /* the temp directory refused the removal; nothing else here can */
290
+ }
291
+ }
292
+
293
+ const review_reviewed_tree = {
294
+ MOVEMENT,
295
+ discard,
296
+ high_cited,
297
+ is_stopped_on,
298
+ is_unmoved,
299
+ mark_stopped,
300
+ movement,
301
+ record,
302
+ resume_point,
303
+ standing_here,
304
+ }
305
+
306
+ export type { Movement, Standing }
307
+ export { review_reviewed_tree }
@@ -0,0 +1,62 @@
1
+ import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
2
+ import { review_finding_ledger, type DisposedLine } from './review-finding-ledger'
3
+
4
+ // Whether round 1's High/Medium findings all closed, read from the issue's own ledger file rather
5
+ // than asserted by the caller (joshuafolkken/kit#5286). Closed means every High/Medium `- rf:` line
6
+ // of the issue carries `fixed` or `false-positive`; a line with no disposition, a filed or deferred
7
+ // one, or no recorded round at all leaves round 1 open, so the answer stays `required`. Only the
8
+ // issue's latest code round is read (joshuafolkken/kit#5359): an earlier round's unfixed lines stay
9
+ // in the append-only ledger after a clean re-run.
10
+
11
+ const BLOCKING_SEVERITIES: ReadonlySet<string> = new Set(['high', 'medium'])
12
+ const NO_RECORD_REASON =
13
+ 'no round-1 round is recorded in the ledger for this issue, so its findings cannot be read as closed'
14
+
15
+ const OPEN_REASON_PREFIX =
16
+ 'round 1 left High/Medium findings neither fixed nor a false positive in the ledger'
17
+ const NO_DISPOSITION = 'no disposition'
18
+
19
+ interface Closure {
20
+ is_closed: boolean
21
+ reason: string
22
+ }
23
+
24
+ const CLOSED: Closure = { is_closed: true, reason: '' }
25
+
26
+ function is_open(line: DisposedLine): boolean {
27
+ return (
28
+ BLOCKING_SEVERITIES.has(line.severity) && !review_finding_ledger.is_closing(line.disposition)
29
+ )
30
+ }
31
+
32
+ function open_reason(open: ReadonlyArray<DisposedLine>): string {
33
+ const dispositions = open.map((line) => line.disposition ?? NO_DISPOSITION).join(', ')
34
+
35
+ return `${OPEN_REASON_PREFIX} (${String(open.length)}: ${dispositions}), so the pass that verifies them is due`
36
+ }
37
+
38
+ function of_content(content: string | undefined, issue: number): Closure {
39
+ const latest = review_finding_ledger.latest_round(content ?? '', issue)
40
+ const lines = review_finding_ledger.issue_dispositions(latest, issue)
41
+
42
+ if (lines.length === 0) return { is_closed: false, reason: NO_RECORD_REASON }
43
+
44
+ const open = lines.filter((line) => is_open(line))
45
+
46
+ return open.length === 0 ? CLOSED : { is_closed: false, reason: open_reason(open) }
47
+ }
48
+
49
+ // `undefined` issue — a branch that names none — has no ledger lines to read, so it is open.
50
+ async function read(
51
+ issue: number | undefined,
52
+ root: string = observation_ledger_home.ledger_root(),
53
+ ): Promise<Closure> {
54
+ if (issue === undefined) return { is_closed: false, reason: NO_RECORD_REASON }
55
+
56
+ return of_content(await observation_ledger_home.read(root), issue)
57
+ }
58
+
59
+ const review_round_one_closure = { NO_RECORD_REASON, OPEN_REASON_PREFIX, of_content, read }
60
+
61
+ export type { Closure }
62
+ export { review_round_one_closure }
@@ -0,0 +1,106 @@
1
+ import { issue_watch_gate } from '#scripts/issue/issue-watch-gate'
2
+ import { error_text } from '#scripts/lib/error-message'
3
+ import { review_record } from './review-record'
4
+ import { review_reviewed_tree } from './review-reviewed-tree'
5
+
6
+ // Whether `josh ship` reuses the recorded round 1 rather than reviewing it again — asked by the ship
7
+ // and by `josh review:record --check`, so a lane resuming on an old record reads the ship's own answer
8
+ // rather than mistaking "recorded" for "reused" (joshuafolkken/kit#4763).
9
+
10
+ // The issue changed after the recorded review was briefed — the refusal `josh followup` would make
11
+ // (joshuafolkken/kit#4221). A read that fails reads as fresh: `josh followup` still refuses a stale
12
+ // review at the merge.
13
+ async function is_stale_here(): Promise<boolean> {
14
+ try {
15
+ return (await issue_watch_gate.review_refusal_here()) !== undefined
16
+ } catch (error) {
17
+ error_text.trace_swallowed('review_round_reuse.is_stale_here', error)
18
+
19
+ return false
20
+ }
21
+ }
22
+
23
+ // What the ship does with the recorded round 1: `held` reuses it with nothing left to read; `delta`
24
+ // reuses it and leaves what moved since to round 2; `again` reviews round 1 again.
25
+ const REUSE = { AGAIN: 'again', DELTA: 'delta', HELD: 'held' } as const
26
+
27
+ type Reuse = (typeof REUSE)[keyof typeof REUSE]
28
+
29
+ const { MOVEMENT } = review_reviewed_tree
30
+
31
+ // The reuse, and on `again` why (joshuafolkken/kit#4941): `review:record --check` prints it, so a lane
32
+ // reads which condition re-runs round 1 rather than a list of every condition that could.
33
+ interface ReuseAnswer {
34
+ reuse: Reuse
35
+ cause?: string | undefined
36
+ }
37
+
38
+ const STALE_CAUSE = "the issue changed after the recorded round's brief"
39
+ const UNRECORDED_CAUSE = 'no review round is recorded for this issue'
40
+
41
+ async function recorded_answer(issue: number): Promise<ReuseAnswer> {
42
+ const { movement, cause } = await review_reviewed_tree.standing_here()
43
+
44
+ if (movement === MOVEMENT.HELD) return { reuse: REUSE.HELD }
45
+
46
+ const is_delta = movement === MOVEMENT.DELTA || review_reviewed_tree.is_stopped_on(issue)
47
+
48
+ return is_delta ? { reuse: REUSE.DELTA } : { reuse: REUSE.AGAIN, cause }
49
+ }
50
+
51
+ // **A recorded round 1 is reused while the tree it was recorded on holds still** — a lane's review
52
+ // before the ship (joshuafolkken/kit#4620) — **or once it moved only where round 2 reads**
53
+ // (joshuafolkken/kit#4878): round 2 reads that delta from the round-1 brief's stamp after the commit, so
54
+ // round 1 run again would read it twice. **The ship's own round 1 that stopped on a finding** is reused
55
+ // however the lane's fix moved the tree, for the same reason. A High-cited file's fix is such a move
56
+ // where round 2's delta holds it (joshuafolkken/kit#4941). Any other move — a doc, a revert — is
57
+ // reviewed again, from that record (`review-reviewed-tree.ts`). An issue that changed after that
58
+ // round's brief is never reused: the recorded round never read it, and `josh followup` refuses the merge on it
59
+ // (joshuafolkken/kit#4221).
60
+ async function answer(issue: number, is_stale: boolean): Promise<ReuseAnswer> {
61
+ if (is_stale) return { reuse: REUSE.AGAIN, cause: STALE_CAUSE }
62
+
63
+ const recorded = await review_record.check(issue)
64
+
65
+ if (recorded.status !== 'ok') return { reuse: REUSE.AGAIN, cause: UNRECORDED_CAUSE }
66
+
67
+ return await recorded_answer(issue)
68
+ }
69
+
70
+ async function reuse(issue: number, is_stale: boolean): Promise<Reuse> {
71
+ const reused = await answer(issue, is_stale)
72
+
73
+ return reused.reuse
74
+ }
75
+
76
+ async function is_reusable(issue: number, is_stale: boolean): Promise<boolean> {
77
+ return (await reuse(issue, is_stale)) !== REUSE.AGAIN
78
+ }
79
+
80
+ // The ship's own answer for this checkout, asked before the ship runs; a git failure throws.
81
+ async function answer_here(issue: number): Promise<ReuseAnswer> {
82
+ return await answer(issue, await is_stale_here())
83
+ }
84
+
85
+ async function reuse_here(issue: number): Promise<Reuse> {
86
+ const reused = await answer_here(issue)
87
+
88
+ return reused.reuse
89
+ }
90
+
91
+ async function is_reusable_here(issue: number): Promise<boolean> {
92
+ return (await reuse_here(issue)) !== REUSE.AGAIN
93
+ }
94
+
95
+ const review_round_reuse = {
96
+ REUSE,
97
+ answer,
98
+ answer_here,
99
+ is_reusable,
100
+ is_reusable_here,
101
+ is_stale_here,
102
+ reuse_here,
103
+ }
104
+
105
+ export type { Reuse, ReuseAnswer }
106
+ export { review_round_reuse }
@@ -1,11 +1,17 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { change_base } from '#scripts/git/change-base'
4
+ import { git_branch } from '#scripts/git/git-branch'
5
+ import { git_command } from '#scripts/git/git-command'
6
+ import { issue_number_shape } from '#scripts/issue/issue-number-shape'
4
7
  import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
5
8
  import { path_decision } from '#scripts/josh/path-decision'
6
9
  import { review_code_tree } from './review-code-tree'
10
+ import { review_reviewed_tree } from './review-reviewed-tree'
7
11
  import { review_round_one_blobs } from './review-round-one-blobs'
12
+ import { review_round_one_closure } from './review-round-one-closure'
8
13
  import { review_round2, type RoundTwoAnswer } from './review-round2'
14
+ import { review_round2_skip_comment } from './review-round2-skip-comment'
9
15
  import { review_stamps } from './review-stamps'
10
16
  import { review_tree } from './review-tree'
11
17
 
@@ -18,42 +24,82 @@ import { review_tree } from './review-tree'
18
24
  // `review-round2.ts`; everything here is the invocation.
19
25
 
20
26
  const ARGV_OFFSET = 2
21
- const USAGE = 'Usage: josh review:round2 [--round-1-closed] [--json]'
27
+ const USAGE = 'Usage: josh review:round2 [--record <N>] [--json]'
22
28
  const FAILURE_EXIT_CODE = 1
23
29
 
24
- // The one input no command can read for itself, so the caller states it: every round-1 High/Medium
25
- // finding closed, by a fix in this working tree or as a verified false positive, and none was filed
26
- // or deferred. Spelled out rather than shortened to `--closed`, because a flag whose meaning has to
27
- // be recalled is one that gets passed on the wrong run.
28
- const CLOSED_FLAG = '--round-1-closed'
29
- const KNOWN_FLAGS: ReadonlyArray<string> = [CLOSED_FLAG, path_decision.JSON_FLAG]
30
+ // **Round 1's closure is read from the ledger, not asserted** (joshuafolkken/kit#5286): the flag that
31
+ // once stated it is refused with the one line that names what replaced it.
32
+ const RETIRED_CLOSED_FLAG = '--round-1-closed'
33
+ const RETIRED_CLOSED_LINE =
34
+ '--round-1-closed is retired: review:round2 reads round 1’s closure from the dispositions `review:record` keeps in the ledger — record each finding as `fixed <spec>` or `false-positive <spec>`, and pass --record <N> to name the issue.'
35
+ const RECORD_FLAG = '--record'
36
+ // The flag and its issue number.
37
+ const RECORD_ARGUMENT_COUNT = 2
38
+ const KNOWN_FLAGS: ReadonlyArray<string> = [path_decision.JSON_FLAG]
30
39
 
31
40
  interface Options {
32
- is_round_one_closed: boolean
33
41
  is_json: boolean
42
+ // The issue `--record` names — whose ledger lines are read and where a skip is commented.
43
+ record: number | undefined
44
+ }
45
+
46
+ function without_record(argv: ReadonlyArray<string>): ReadonlyArray<string> | undefined {
47
+ const index = argv.indexOf(RECORD_FLAG)
48
+
49
+ if (index === -1) return argv
50
+
51
+ const value = argv[index + 1] ?? ''
52
+
53
+ return issue_number_shape.is_issue_number(value)
54
+ ? argv.toSpliced(index, RECORD_ARGUMENT_COUNT)
55
+ : undefined
34
56
  }
35
57
 
36
58
  // `undefined` on anything unrecognized rather than a default, through the contract
37
- // `path_decision.has_unknown_flag` states once for every mechanically-decided command: a misspelled
38
- // `--round1-closed` that silently answered `required` would be harmless, but a parser that guesses
39
- // is what makes the difference a matter of which flag was fumbled.
59
+ // `path_decision.has_unknown_flag` states once for every mechanically-decided command: a parser that
60
+ // guesses is what makes the answer a matter of which flag was fumbled.
40
61
  function parse_options(argv: ReadonlyArray<string>): Options | undefined {
41
- if (path_decision.has_unknown_flag(argv, KNOWN_FLAGS)) return undefined
62
+ const rest = without_record(argv)
63
+
64
+ if (rest === undefined || path_decision.has_unknown_flag(rest, KNOWN_FLAGS)) return undefined
65
+
66
+ const index = argv.indexOf(RECORD_FLAG)
42
67
 
43
68
  return {
44
- is_round_one_closed: argv.includes(CLOSED_FLAG),
45
- is_json: argv.includes(path_decision.JSON_FLAG),
69
+ is_json: rest.includes(path_decision.JSON_FLAG),
70
+ record: index === -1 ? undefined : Number(argv[index + 1]),
46
71
  }
47
72
  }
48
73
 
49
- // Only the paths the code record covers are re-read, so a run without one parses nothing.
74
+ // The issue whose ledger lines decide round 1's closure: `--record`'s, else the branch's.
75
+ async function issue_of(options: Options): Promise<number | undefined> {
76
+ return options.record ?? git_branch.issue_from_branch(await git_command.branch())
77
+ }
78
+
79
+ async function report_record(issue: number, answer: RoundTwoAnswer): Promise<void> {
80
+ const recorded = await review_round2_skip_comment.record(issue, answer)
81
+
82
+ if (recorded === 'posted' || recorded === 'present') {
83
+ console.error(`${review_round2_skip_comment.HEADING}: ${recorded} on issue ${String(issue)}`)
84
+ } else if (recorded !== 'not-skipped') {
85
+ console.error(`${review_round2_skip_comment.HEADING}: not recorded (${recorded})`)
86
+ }
87
+ }
88
+
89
+ // Only the paths the code record covers are re-read, so a run without one parses nothing. **A file a
90
+ // recorded High cites is never comment-only** (joshuafolkken/kit#4941): `josh ship` reuses round 1 once
91
+ // that file's fix moved (`review-reviewed-tree.ts`), so a fix written in comments alone is still this
92
+ // round's to verify.
50
93
  async function read_comment_only(base: string | undefined): Promise<ReadonlyArray<string>> {
51
94
  const snapshot = review_stamps.round_one_code_stamp.read()
52
95
  const paths = Object.keys(snapshot?.files ?? {})
53
96
  const tree = await review_code_tree.read(paths)
54
97
  const unmoved = review_round_one_blobs.unmoved({ from: snapshot?.base, to: base, paths })
98
+ const cited = new Set(review_reviewed_tree.high_cited())
55
99
 
56
- return review_code_tree.comment_only(snapshot, tree, base, unmoved)
100
+ return review_code_tree
101
+ .comment_only(snapshot, tree, base, unmoved)
102
+ .filter((path) => !cited.has(path))
57
103
  }
58
104
 
59
105
  // The carry over every path changed now, not only round 1's: a file the fix added after the merge is
@@ -82,34 +128,48 @@ function print(answer: RoundTwoAnswer, is_json: boolean): void {
82
128
  path_decision.print_decision(review_round2.JSON_KEY, answer.verdict, answer.reason, is_json)
83
129
  }
84
130
 
85
- async function run(argv: ReadonlyArray<string>): Promise<number> {
86
- const options = parse_options(argv)
87
-
88
- if (options === undefined) {
89
- console.error(USAGE)
90
-
91
- return FAILURE_EXIT_CODE
92
- }
131
+ function refusal_of(argv: ReadonlyArray<string>): string {
132
+ return argv.includes(RETIRED_CLOSED_FLAG) ? RETIRED_CLOSED_LINE : USAGE
133
+ }
93
134
 
135
+ async function decide_here(options: Options): Promise<RoundTwoAnswer> {
136
+ const closure = await review_round_one_closure.read(await issue_of(options))
94
137
  // The commit the tree below is a diff against, so the snapshot can be refused when it was taken
95
138
  // against a different one — and the resolved commit rather than `git_command.change_base`, whose
96
- // fallback is a ref name that moves and would compare equal to itself.
97
- const base = await change_base.resolved()
139
+ // fallback is a ref name that moves and would compare equal to itself. Read through a merge in
140
+ // progress, as the changed tree below is.
141
+ const base = await change_base.resolved_through_merge()
98
142
  // No paths argument: `read_changed_tree` reads them itself, from the one definition of "changed"
99
143
  // `josh review:level` and `josh review:brief` all decide from. Passing our own reading would be a
100
144
  // second definition, which is the drift that module exists to prevent.
101
145
  const tree = await review_tree.read_changed_tree()
102
- const decision = review_round2.decide({
146
+
147
+ return review_round2.decide({
103
148
  base,
104
149
  carried_delta: carried_reader(base, tree),
105
150
  comment_only: await read_comment_only(base),
106
- is_round_one_closed: options.is_round_one_closed,
151
+ is_round_one_closed: closure.is_closed,
152
+ open_reason: closure.reason,
107
153
  snapshot: review_stamps.round_one_stamp.read(),
108
154
  tree,
109
155
  })
156
+ }
157
+
158
+ async function run(argv: ReadonlyArray<string>): Promise<number> {
159
+ const options = parse_options(argv)
160
+
161
+ if (options === undefined) {
162
+ console.error(refusal_of(argv))
163
+
164
+ return FAILURE_EXIT_CODE
165
+ }
166
+
167
+ const decision = await decide_here(options)
110
168
 
111
169
  print(decision, options.is_json)
112
170
 
171
+ if (options.record !== undefined) await report_record(options.record, decision)
172
+
113
173
  return 0
114
174
  }
115
175
 
@@ -118,11 +178,13 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
118
178
  }
119
179
 
120
180
  const review_round2_cli = {
121
- CLOSED_FLAG,
122
181
  carried_reader,
123
182
  parse_options,
124
183
  print,
125
184
  read_comment_only,
185
+ RECORD_FLAG,
186
+ RETIRED_CLOSED_FLAG,
187
+ RETIRED_CLOSED_LINE,
126
188
  run,
127
189
  }
128
190