@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,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 work summary in, e.g. `pnpm josh report:lint < summary.md`'
18
+ '✖ nothing on stdin — pipe the summary in, e.g. `pnpm josh report:lint < summary.md`'
15
19
 
16
- function run(summary: string): number {
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 = report_lint.lint_report(summary)
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(report_lint.with_reference(violations).join('\n'))
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
- if (process.stdin.isTTY) return run('')
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
- const POINTER_PREFIX = 'josh-review-target-'
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(POINTER_PREFIX, root)
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
- function record_target(checkout: ReviewCheckout, root?: string): string {
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
- drop_records(read_pointer(root))
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 check(now: number = Date.now(), root?: string): AttestVerdict {
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 pointer = read_pointer(root)
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 }