@joshuafolkken/kit 1.1010.0 → 1.1050.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 (350) hide show
  1. package/.claude/settings.json +1 -5
  2. package/.claude/skills/dependency-update/SKILL.md +6 -3
  3. package/.claude/skills/epic-commands/SKILL.md +46 -17
  4. package/.claude/skills/verify-ui/SKILL.md +1 -1
  5. package/.claude/skills/workflow-commands/SKILL.md +10 -25
  6. package/.claude/skills/workflow-commands/background-commands.md +1 -1
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -36
  11. package/.claude/skills/workflow-commands/backlogrun-steps.md +16 -16
  12. package/.claude/skills/workflow-commands/backlogrun.md +3 -12
  13. package/.claude/skills/workflow-commands/chain-rule.md +2 -2
  14. package/.claude/skills/workflow-commands/followup-reference.md +2 -2
  15. package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
  16. package/.claude/skills/workflow-commands/fullrun.md +1 -1
  17. package/.claude/skills/workflow-commands/halfrun.md +2 -1
  18. package/.claude/skills/workflow-commands/into-target.md +1 -1
  19. package/.claude/skills/workflow-commands/issue-comments.md +1 -1
  20. package/.claude/skills/workflow-commands/observation-filing.md +80 -126
  21. package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
  22. package/.claude/skills/workflow-commands/prerequisite.md +1 -1
  23. package/.claude/skills/workflow-commands/working-tree-hold.md +2 -2
  24. package/.codex/hooks.json +1 -5
  25. package/.github/workflows/auto-tag.yml +21 -0
  26. package/.github/workflows/publish.yml +9 -10
  27. package/.github/workflows/sonar-qube.yml +1 -1
  28. package/AGENTS.md +4 -25
  29. package/CLAUDE.md +24 -38
  30. package/GEMINI.md +4 -24
  31. package/README.md +22 -56
  32. package/dist/CLAUDE.md +24 -38
  33. package/dist/hooks/batch-guard.js +4 -4
  34. package/dist/hooks/{chunk-6PSTVOLH.js → chunk-5BK3K7V4.js} +114 -90
  35. package/dist/hooks/{chunk-PAXMLGSS.js → chunk-7VPDUBRN.js} +93 -175
  36. package/dist/hooks/{chunk-KPZN42RT.js → chunk-DMQN45FN.js} +3 -3
  37. package/dist/hooks/{chunk-WMIR5SNX.js → chunk-EJDX2TOB.js} +13 -3
  38. package/dist/hooks/{chunk-A2H5I2VD.js → chunk-GQ7KP45M.js} +2 -2
  39. package/dist/hooks/{chunk-4GDR2YTV.js → chunk-HJIISEAC.js} +4 -4
  40. package/dist/hooks/{chunk-AWTFKKFW.js → chunk-NAS4C7GP.js} +6 -33
  41. package/dist/hooks/{chunk-56LGMJJV.js → chunk-NJQOBE6L.js} +2 -2
  42. package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-RLDX5SOC.js} +1222 -941
  43. package/dist/hooks/{chunk-GBJUERYK.js → chunk-WQ4OSBSS.js} +10 -3
  44. package/dist/hooks/codex-hook-adapter.js +10 -10
  45. package/dist/hooks/duplicate-read-guard.js +5 -5
  46. package/dist/hooks/format-edited.js +15 -34
  47. package/dist/hooks/investigation-guard.js +5 -5
  48. package/dist/hooks/pretool-guard.js +23 -16
  49. package/dist/hooks/stop-guard.js +21 -200
  50. package/dist/josh.js +42 -125
  51. package/dist/version/index.js +466 -42
  52. package/eslint/base.js +2 -11
  53. package/package.json +36 -10
  54. package/prompts/coding-standards.md +15 -290
  55. package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
  56. package/prompts/collaboration-workflow/epic-audit.md +2 -53
  57. package/prompts/collaboration-workflow/epic-bundle.md +2 -106
  58. package/prompts/collaboration-workflow/file-edits.md +6 -53
  59. package/prompts/collaboration-workflow/issue-template.md +4 -4
  60. package/prompts/collaboration-workflow/operating-rules.md +18 -75
  61. package/prompts/collaboration-workflow/principles.md +74 -0
  62. package/prompts/collaboration-workflow/residency.md +55 -15
  63. package/prompts/collaboration-workflow/rule-delivery.md +5 -46
  64. package/prompts/collaboration-workflow/shell-body.md +7 -9
  65. package/prompts/collaboration-workflow/turn-batching.md +21 -37
  66. package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
  67. package/prompts/collaboration-workflow/wip-cap.md +15 -18
  68. package/prompts/collaboration-workflow.md +22 -28
  69. package/prompts/sonar-hotspot-handling.md +1 -1
  70. package/scripts/adopt/adopt-logic.ts +2 -2
  71. package/scripts/adopt/adopt-toolkits.ts +3 -1
  72. package/scripts/adopt/adopt.ts +2 -2
  73. package/scripts/agent/agent-event.ts +2 -9
  74. package/scripts/agent/agent-exit-record.ts +2 -9
  75. package/scripts/auto-ok/auto-ok-cli.ts +9 -9
  76. package/scripts/backlog/backlog-budget-cli.ts +2 -6
  77. package/scripts/backlog/backlog-drive-cli.ts +20 -10
  78. package/scripts/backlog/backlog-drive.ts +50 -16
  79. package/scripts/backlog/backlog-next.ts +24 -8
  80. package/scripts/backlog/backlog-offer-cli.ts +6 -5
  81. package/scripts/backlog/backlog-plan-cli.ts +5 -1
  82. package/scripts/backlog/backlog-plan.ts +1 -1
  83. package/scripts/backlog/backlog-pool.ts +31 -11
  84. package/scripts/backlog/backlog-rank.ts +106 -0
  85. package/scripts/backlog/backlog-waves.ts +42 -29
  86. package/scripts/build/build-bin.ts +2 -1
  87. package/scripts/build/build-config-merge.ts +2 -1
  88. package/scripts/build/build-hooks.ts +31 -6
  89. package/scripts/build/build-managed-marker.ts +2 -1
  90. package/scripts/build/build-self-sync-guard.ts +2 -1
  91. package/scripts/build/build-version.ts +2 -1
  92. package/scripts/cases/cases-logic.ts +2 -6
  93. package/scripts/ci/pr-classification.ts +13 -11
  94. package/scripts/cost-runtime/cost-transcript.ts +4 -11
  95. package/scripts/delegation/delegation-policy.ts +11 -0
  96. package/scripts/doctor/doctor-consumer.ts +5 -12
  97. package/scripts/doctor/doctor-ports.ts +2 -2
  98. package/scripts/document/document-byte-budget.ts +13 -21
  99. package/scripts/document/document-section.ts +2 -6
  100. package/scripts/document/entry-read-budget.ts +6 -5
  101. package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
  102. package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
  103. package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
  104. package/scripts/epic/epic-audit-cli.ts +4 -4
  105. package/scripts/epic/epic-audit-orphans.ts +2 -2
  106. package/scripts/epic/epic-audit-rationale.ts +1 -1
  107. package/scripts/epic/epic-audit.ts +1 -1
  108. package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
  109. package/scripts/epic/epic-bundle-cli.ts +1 -1
  110. package/scripts/epic/epic-bundle.ts +2 -2
  111. package/scripts/epic/epic-candidate-confirm.ts +2 -1
  112. package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
  113. package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
  114. package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
  115. package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
  116. package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
  117. package/scripts/epic/epic-cross-repo.ts +21 -38
  118. package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
  119. package/scripts/epic/epic-fetch.ts +3 -3
  120. package/scripts/epic/epic-graph.ts +3 -3
  121. package/scripts/epic/epic-index.ts +2 -2
  122. package/scripts/epic/epic-issue.ts +1 -1
  123. package/scripts/epic/epic-lane-offer.ts +11 -22
  124. package/scripts/epic/epic-next.ts +3 -3
  125. package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
  126. package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
  127. package/scripts/epic/epic-rank.ts +37 -0
  128. package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
  129. package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
  130. package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
  131. package/scripts/epic/epic-relation-recheck.ts +4 -3
  132. package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
  133. package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
  134. package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
  135. package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
  136. package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
  137. package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
  138. package/scripts/epic/epic-solo.ts +29 -28
  139. package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
  140. package/{scripts-ai → scripts/epic}/epic.ts +17 -16
  141. package/scripts/gh/fix-gh-packages.ts +2 -1
  142. package/scripts/gh/gh-spawn.ts +12 -10
  143. package/scripts/git/constants.ts +1 -1
  144. package/scripts/git/git-branch.ts +10 -1
  145. package/scripts/git/git-command.ts +2 -2
  146. package/scripts/git/git-error.ts +2 -5
  147. package/scripts/git/git-execa-error.ts +6 -1
  148. package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
  149. package/scripts/git/git-followup-flush.ts +74 -48
  150. package/scripts/git/git-followup-issue-close.ts +2 -2
  151. package/scripts/git/git-followup-label.ts +1 -1
  152. package/scripts/git/git-followup-pending.ts +1 -1
  153. package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
  154. package/scripts/git/git-gh-exec.ts +85 -26
  155. package/scripts/git/git-gh-issue-read.ts +2 -2
  156. package/scripts/git/git-gh-issue-rest.ts +2 -2
  157. package/scripts/git/git-gh-issue-write.ts +6 -6
  158. package/scripts/git/git-gh-pr-read.ts +0 -21
  159. package/scripts/git/git-gh-pr.ts +2 -23
  160. package/scripts/git/git-next-issues.ts +34 -5
  161. package/scripts/git/git-pr-followup-wrapup.ts +2 -6
  162. package/scripts/git/git-preflight.ts +25 -10
  163. package/scripts/git/git-push-transport.ts +12 -69
  164. package/scripts/git/git-spawn.ts +79 -3
  165. package/scripts/git/git-ssh-keepalive.ts +74 -0
  166. package/scripts/git/git-staging.ts +21 -9
  167. package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
  168. package/scripts/git/github-issue-url.ts +1 -1
  169. package/scripts/git/gone-branch.ts +3 -2
  170. package/scripts/git/issue-labels.ts +13 -1
  171. package/scripts/git/issue-rank.ts +89 -0
  172. package/scripts/git/issue-reference.ts +19 -0
  173. package/scripts/git/main-merge.ts +2 -1
  174. package/scripts/git/main-sync.ts +3 -2
  175. package/scripts/git/schemas.ts +1 -1
  176. package/scripts/git/stash-pop-args.ts +7 -13
  177. package/scripts/git/stash-pop-cli.ts +2 -1
  178. package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
  179. package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
  180. package/scripts/hooks/format-edited-cli.ts +15 -0
  181. package/scripts/hooks/format-edited-file.ts +2 -9
  182. package/scripts/hooks/format-rewrite-notice.ts +2 -8
  183. package/scripts/hooks/pretool-guard-cli.ts +21 -0
  184. package/scripts/hooks/pretool-guard.ts +14 -15
  185. package/scripts/init/init-ai-copy.ts +2 -5
  186. package/scripts/init/init.ts +8 -4
  187. package/scripts/init/start-exec.ts +7 -1
  188. package/scripts/init/start-plan.ts +18 -5
  189. package/scripts/init/start-setup-pr.ts +46 -19
  190. package/scripts/init/start-steps.ts +43 -10
  191. package/scripts/init/start.ts +42 -11
  192. package/scripts/issue/issue-cite.ts +19 -1
  193. package/scripts/issue/issue-comment-cli.ts +2 -1
  194. package/scripts/issue/issue-file-cli.ts +2 -1
  195. package/scripts/issue/issue-fold-cli.ts +9 -16
  196. package/scripts/issue/issue-scout-cli.ts +8 -13
  197. package/scripts/issue/measure-rerun-cli.ts +7 -6
  198. package/scripts/josh/josh-command-map.ts +0 -102
  199. package/scripts/josh/josh-commands-ai.ts +2 -2
  200. package/scripts/josh/josh-commands-backlog.ts +1 -1
  201. package/scripts/josh/josh-commands-development.ts +3 -3
  202. package/scripts/josh/josh-commands-project.ts +1 -1
  203. package/scripts/josh/josh-commands-workflow.ts +4 -4
  204. package/scripts/lane/lane-child-marker.ts +16 -2
  205. package/scripts/lane/lane-cli.ts +34 -15
  206. package/scripts/lane/lane-open.ts +3 -2
  207. package/scripts/lane/lane-registry.ts +3 -6
  208. package/scripts/lane/lane-report.ts +10 -4
  209. package/scripts/lib/cli-flags.ts +20 -2
  210. package/scripts/lib/error-message.ts +9 -0
  211. package/scripts/lib/read-file.ts +26 -2
  212. package/scripts/observations/observation-ledger-home.ts +98 -26
  213. package/scripts/observations/observation-ledger-migrate.ts +26 -21
  214. package/scripts/observations/observation-ledger-prepare.ts +10 -20
  215. package/scripts/observations/observation-ledger.ts +58 -25
  216. package/scripts/observations/observations-flush-cli.ts +6 -23
  217. package/scripts/observations/observations-flush-landing.ts +2 -1
  218. package/scripts/observations/observations-flush.ts +29 -20
  219. package/scripts/package/package-scout-cli.ts +8 -16
  220. package/scripts/ports/port-command.ts +2 -1
  221. package/scripts/propagate/propagate-plan.ts +185 -0
  222. package/scripts/propagate/propagate-publish.ts +6 -7
  223. package/scripts/propagate/propagate-run.ts +5 -1
  224. package/scripts/propagate/propagate-steps.ts +18 -2
  225. package/scripts/propagate/propagate-targets.ts +13 -2
  226. package/scripts/propagate/propagate.ts +63 -44
  227. package/scripts/refactor/import-graph.ts +2 -6
  228. package/scripts/registry-migration/migrate-logic.ts +1 -0
  229. package/scripts/registry-migration/migrate.ts +3 -2
  230. package/scripts/registry-migration/user-npmrc.ts +2 -2
  231. package/scripts/release/github-release.ts +1 -1
  232. package/scripts/release/release-cli.ts +2 -1
  233. package/scripts/repo/repo-setting.ts +10 -8
  234. package/scripts/review/review-attest.ts +44 -0
  235. package/scripts/review/review-brief-cli.ts +49 -2
  236. package/scripts/review/review-brief.ts +46 -1
  237. package/scripts/review/review-finding-ledger.ts +3 -3
  238. package/scripts/review/review-findings-cli.ts +4 -11
  239. package/scripts/review/review-record-cli.ts +14 -11
  240. package/scripts/review/review-record.ts +6 -16
  241. package/scripts/review/review-stamps.ts +7 -0
  242. package/scripts/rules/decision-oracle.ts +2 -2
  243. package/scripts/rules/implementation-cut.ts +1 -1
  244. package/scripts/rules/issue-fold.ts +1 -2
  245. package/scripts/rules/lane-carry-conflict.ts +1 -2
  246. package/scripts/rules/lane-park.ts +1 -2
  247. package/scripts/rules/pre-gate-cut.ts +2 -2
  248. package/scripts/rules/rule-body-guard.ts +2 -2
  249. package/scripts/rules/rule-value-cache.ts +99 -0
  250. package/scripts/rules/rule-value-cli.ts +10 -9
  251. package/scripts/rules/rule-value.ts +1 -1
  252. package/scripts/run/run-carry-args.ts +2 -6
  253. package/scripts/run/run-carry-cli.ts +5 -7
  254. package/scripts/run/run-cut-args.ts +7 -13
  255. package/scripts/run/run-ending-cli.ts +4 -7
  256. package/scripts/run/run-event-stream.ts +2 -5
  257. package/scripts/run/run-headless.ts +22 -6
  258. package/scripts/run/run-liveness-cli.ts +4 -7
  259. package/scripts/run/run-parent-cut-hook.ts +144 -0
  260. package/scripts/run/run-progress-args.ts +2 -6
  261. package/scripts/run/run-progress-cli.ts +2 -1
  262. package/scripts/run/run-progress-read.ts +2 -1
  263. package/scripts/run/run-progress.ts +21 -8
  264. package/scripts/run/run-ship-cli.ts +6 -74
  265. package/scripts/run/run-ship-preflight.ts +87 -0
  266. package/scripts/run/run-ship-review-steps.ts +5 -26
  267. package/scripts/run/run-ship-review.ts +9 -0
  268. package/scripts/run/run-ship-scoped.ts +44 -0
  269. package/scripts/run/run-ship-stage.ts +5 -1
  270. package/scripts/run/run-ship-steps.ts +105 -0
  271. package/scripts/run/run-ship.ts +2 -0
  272. package/scripts/run/run-tail-cli.ts +3 -3
  273. package/scripts/run/run-tidy-stashes.ts +7 -14
  274. package/scripts/run/run-wake-cli.ts +4 -8
  275. package/scripts/run/run-wake-describe.ts +19 -1
  276. package/scripts/run/run-wake-driver.ts +15 -2
  277. package/scripts/run/run-wake-session.ts +14 -4
  278. package/scripts/run/run-watcher-hook.ts +1 -1
  279. package/scripts/safe-chain/project-config.ts +3 -6
  280. package/scripts/security/security-audit-provision.ts +6 -7
  281. package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
  282. package/scripts/sync/directory-copy-guard.ts +2 -1
  283. package/scripts/sync/sync-ai-files.ts +3 -3
  284. package/scripts/sync/template-source-logic.ts +3 -4
  285. package/scripts/test/e2e-retry-check.ts +3 -6
  286. package/scripts/time-runtime/time-bundle-call.ts +2 -2
  287. package/scripts/ui/ui-routes-cli.ts +3 -6
  288. package/scripts/version/bump-version.ts +2 -1
  289. package/scripts/version/npm-registry.ts +97 -0
  290. package/scripts/version/version-commands.ts +5 -4
  291. package/scripts/version/version-remote.ts +38 -33
  292. package/scripts/version/version-targets.ts +3 -3
  293. package/.claude/skills/workflow-commands/rule-residency.md +0 -267
  294. package/evals/scenarios/consult-not-execute.json +0 -20
  295. package/evals/scenarios/no-implicit-workflow.json +0 -26
  296. package/evals/scenarios/overrides-both-locations.json +0 -29
  297. package/evals/scenarios/upstream-interrupt.json +0 -23
  298. package/evals/scenarios/workflow-skill-loaded.json +0 -16
  299. package/prompts/agent-lint-protocol.json +0 -79
  300. package/prompts/agent-rules.md +0 -63
  301. package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
  302. package/prompts/collaboration-workflow/distributed-docs.md +0 -7
  303. package/prompts/collaboration-workflow/durable-rules.md +0 -24
  304. package/prompts/collaboration-workflow/latest-first.md +0 -14
  305. package/prompts/collaboration-workflow/no-clones.md +0 -11
  306. package/prompts/collaboration-workflow/simplicity-first.md +0 -9
  307. package/prompts/collaboration-workflow/single-source-rules.md +0 -13
  308. package/scripts/build/shim-shell.ts +0 -25
  309. package/scripts/claude/skill-meta.ts +0 -37
  310. package/scripts/document/document-scan.ts +0 -108
  311. package/scripts/document/generate-catalog.ts +0 -69
  312. package/scripts/document/retired-phrases.ts +0 -143
  313. package/scripts/document/review-agent-type.ts +0 -46
  314. package/scripts/document/single-source.ts +0 -73
  315. package/scripts/git/git-fixture-workspace.ts +0 -88
  316. package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
  317. package/scripts/hooks/prompt-hooks.ts +0 -38
  318. package/scripts/init/hook-command-bootstrap.ts +0 -83
  319. package/scripts/issue/issue-logic.ts +0 -50
  320. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
  321. package/scripts/josh/josh-script-reader.ts +0 -11
  322. package/scripts/package/production-dependency-graph.ts +0 -68
  323. package/scripts/retrospective/retrospective-cli.ts +0 -97
  324. package/scripts/retrospective/retrospective.ts +0 -127
  325. package/scripts/rules/delivered-rules-harness.ts +0 -72
  326. package/scripts/rules/top-level-scripts-guard.ts +0 -47
  327. package/scripts/run/run-carry-flush.ts +0 -58
  328. package/scripts/run/run-transcript.golden.txt +0 -57
  329. package/scripts/run/run-wake-work.ts +0 -108
  330. package/scripts/sync/dependabot-pin-logic.ts +0 -135
  331. package/scripts/test/classify-isolation.ts +0 -149
  332. package/scripts/test/http-fault-injection.ts +0 -79
  333. package/scripts/test/josh-harness-environment.ts +0 -260
  334. package/scripts/test/josh-harness.ts +0 -96
  335. package/scripts/test/pilot-files.ts +0 -452
  336. package/scripts/test/pilot-measure.ts +0 -78
  337. package/scripts/test/test-network-guard.ts +0 -453
  338. package/scripts/test/test-repository-guard.ts +0 -317
  339. package/scripts/test/test-state-guard.ts +0 -85
  340. package/scripts/test/test-stdout-guard.ts +0 -54
  341. package/scripts/test/test-telegram-guard.ts +0 -90
  342. package/scripts/test/unit-guard-environment.ts +0 -12
  343. package/scripts/test/unit-projects.ts +0 -139
  344. package/scripts/test/vitest-include-globs.ts +0 -12
  345. package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
  346. package/scripts-ai/completion-gate.ts +0 -13
  347. package/scripts-ai/environment-loader.ts +0 -9
  348. /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
  349. /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
  350. /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env tsx
2
- import { readFile } from 'node:fs/promises'
3
2
  import { fileURLToPath } from 'node:url'
4
3
  import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
5
4
  import { review_finding_ledger, type CategoryCount } from './review-finding-ledger'
@@ -29,16 +28,10 @@ function format_report(counts: ReadonlyArray<CategoryCount>, zero_rounds: number
29
28
  return `findings by category:\n${body}\n${zero_line(zero_rounds)}`
30
29
  }
31
30
 
32
- async function read_ledger(ledger_path: string): Promise<string> {
33
- try {
34
- return await readFile(ledger_path, 'utf8')
35
- } catch {
36
- return ''
37
- }
38
- }
39
-
40
- async function run(ledger_path: string = observation_ledger_home.ledger_path()): Promise<number> {
41
- const content = await read_ledger(ledger_path)
31
+ // Every issue's file of the ledger directory is counted (joshuafolkken/kit#2919): a recurring category
32
+ // is one whichever issue recorded each sighting.
33
+ async function run(root: string = observation_ledger_home.ledger_root()): Promise<number> {
34
+ const content = (await observation_ledger_home.read(root)) ?? ''
42
35
  const counts = review_finding_ledger.category_counts(content)
43
36
 
44
37
  console.info(format_report(counts, review_finding_ledger.zero_round_count(content)))
@@ -7,8 +7,8 @@ import { review_record, type RecordVerdict } from './review-record'
7
7
 
8
8
  // `josh review:record --issue <N> [<category>:<severity>:<file> ...]` — the one write path for a
9
9
  // `/code-review` round's findings (joshuafolkken/kit#2325). It appends a `- rf:` line per finding to
10
- // the observation ledger, which `pnpm josh observations:flush` then commits like any other ledger
11
- // change. **A call with no findings is a zero-finding round, and it still writes one line** — so the
10
+ // the issue's own file of the observation ledger, which the run's commit carries like any other
11
+ // ledger change. **A call with no findings is a zero-finding round, and it still writes one line** — so the
12
12
  // round that found nothing is recorded rather than mistaken for a round nobody reviewed.
13
13
  //
14
14
  // `josh review:record --check --issue <N>` is the read half `pnpm josh followup` runs before it
@@ -121,14 +121,14 @@ function check_line(verdict: RecordVerdict): string {
121
121
  return verdict.status === 'ok' ? RECORDED_LINE : NOT_REQUIRED_LINE
122
122
  }
123
123
 
124
- async function run_check(issue: string | undefined, ledger_path: string): Promise<number> {
124
+ async function run_check(issue: string | undefined, root: string): Promise<number> {
125
125
  if (issue === undefined || !ISSUE_PATTERN.test(issue)) {
126
126
  console.error(CHECK_USAGE)
127
127
 
128
128
  return FAILURE_EXIT_CODE
129
129
  }
130
130
 
131
- const verdict = await review_record.check(Number(issue), ledger_path)
131
+ const verdict = await review_record.check(Number(issue), root)
132
132
 
133
133
  if (verdict.status === 'ok' || verdict.status === 'not-required') {
134
134
  console.info(check_line(verdict))
@@ -141,7 +141,9 @@ async function run_check(issue: string | undefined, ledger_path: string): Promis
141
141
  return FAILURE_EXIT_CODE
142
142
  }
143
143
 
144
- async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promise<number> {
144
+ // The round lands in its own issue's file (joshuafolkken/kit#2919), so two lanes recording at once
145
+ // write two files, and their pull requests never conflict on the ledger.
146
+ async function run_record(parsed: Parsed, now: Date, root: string): Promise<number> {
145
147
  const request = to_request(parsed)
146
148
 
147
149
  if (request === undefined) {
@@ -151,6 +153,7 @@ async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promi
151
153
  }
152
154
 
153
155
  const lines = build_lines(request, today(now))
156
+ const ledger_path = observation_ledger_home.issue_path(request.issue, root)
154
157
 
155
158
  await observation_ledger_home.append(ledger_path, lines)
156
159
  console.info(confirmation(lines.length, ledger_path))
@@ -158,13 +161,13 @@ async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promi
158
161
  return 0
159
162
  }
160
163
 
161
- // **The default is the primary checkout's ledger, even inside a lane** (joshuafolkken/kit#2419). A line
162
- // appended to a lane's own copy never reached the default branch — the lane's flush is refused and
163
- // `lane:close` does not carry the file — while `--check` read that same copy and let the merge through.
164
+ // **The default is the work tree the command runs in, a lane's inside a lane** (joshuafolkken/kit#2919).
165
+ // The line rides that tree's own commit or `pnpm josh followup`'s pre-merge ledger commit, and
166
+ // `--check` reads the same tree, so the two halves can never disagree about where the round is.
164
167
  async function run(
165
168
  argv: ReadonlyArray<string>,
166
169
  now: Date,
167
- ledger_path: string = observation_ledger_home.ledger_path(),
170
+ root: string = observation_ledger_home.ledger_root(),
168
171
  ): Promise<number> {
169
172
  const parsed = parse_argv(argv)
170
173
 
@@ -174,9 +177,9 @@ async function run(
174
177
  return FAILURE_EXIT_CODE
175
178
  }
176
179
 
177
- if (parsed.check) return await run_check(parsed.issue, ledger_path)
180
+ if (parsed.check) return await run_check(parsed.issue, root)
178
181
 
179
- return await run_record(parsed, now, ledger_path)
182
+ return await run_record(parsed, now, root)
180
183
  }
181
184
 
182
185
  async function main(argv: ReadonlyArray<string>): Promise<void> {
@@ -1,4 +1,3 @@
1
- import { readFile } from 'node:fs/promises'
2
1
  import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
3
2
  import { review_finding_ledger } from './review-finding-ledger'
4
3
 
@@ -29,24 +28,15 @@ interface RecordVerdict {
29
28
  issue?: number
30
29
  }
31
30
 
32
- // `undefined` for both "the file is absent" and "the file could not be read" — they are the same
33
- // answer here, a ledger this checkout does not keep, and the caller turns that into `not-required`.
34
- async function read_ledger(ledger_path: string): Promise<string | undefined> {
35
- try {
36
- return await readFile(ledger_path, 'utf8')
37
- } catch {
38
- return undefined
39
- }
40
- }
41
-
42
- // The default is the primary checkout's ledger, the one `review:record` writes from a lane
43
- // (joshuafolkken/kit#2419) — a cwd-relative default read the lane's copy, so the followup gate refused
44
- // every round recorded from a lane (joshuafolkken/kit#2431).
31
+ // The default is the work tree the check runs in — the one `review:record` wrote to, a lane's in a
32
+ // lane (joshuafolkken/kit#2919). Every file of the directory is read, so the round is found whichever
33
+ // file holds it; a directory that is not there answers `undefined`, a ledger this checkout does not
34
+ // keep, which becomes `not-required`.
45
35
  async function check(
46
36
  issue: number,
47
- ledger_path: string = observation_ledger_home.ledger_path(),
37
+ root: string = observation_ledger_home.ledger_root(),
48
38
  ): Promise<RecordVerdict> {
49
- const content = await read_ledger(ledger_path)
39
+ const content = await observation_ledger_home.read(root)
50
40
 
51
41
  if (content === undefined) return { status: 'not-required' }
52
42
  if (review_finding_ledger.has_issue_record(content, issue)) return { status: 'ok', issue }
@@ -59,10 +59,15 @@ const TEST_RELATED_PREFIX = 'josh-test-related-stamp-'
59
59
  const GATE_PREFIX = 'josh-gate-stamp-'
60
60
  const IN_FLIGHT_PREFIX = 'josh-gate-running-'
61
61
  const ROUND_ONE_PREFIX = 'josh-review-round1-'
62
+ const BRIEFED_PREFIX = 'josh-review-briefed-'
62
63
 
63
64
  const gate_stamp: FileMapStampAccess = file_map_stamp.create(GATE_PREFIX, PROJECT_ROOT)
64
65
  const in_flight_stamp: FileMapStampAccess = file_map_stamp.create(IN_FLIGHT_PREFIX, PROJECT_ROOT)
65
66
  const round_one_stamp: FileMapStampAccess = file_map_stamp.create(ROUND_ONE_PREFIX, PROJECT_ROOT)
67
+ // **The tree the latest round-1 brief handed out, retaken on every brief** (joshuafolkken/kit#2945).
68
+ // The round-1 snapshot is kept from the first brief of a run, so it is not the tree a later review
69
+ // read — a resume narrowed against it would skip a file put back to that older content.
70
+ const briefed_stamp: FileMapStampAccess = file_map_stamp.create(BRIEFED_PREFIX, PROJECT_ROOT)
66
71
  const lint_related_stamp: FileMapStampAccess = file_map_stamp.create(
67
72
  LINT_RELATED_PREFIX,
68
73
  PROJECT_ROOT,
@@ -97,6 +102,8 @@ function clear_round_one(target?: string): void {
97
102
  }
98
103
 
99
104
  const review_stamps = {
105
+ BRIEFED_PREFIX,
106
+ briefed_stamp,
100
107
  clear_round_one,
101
108
  GATE_PREFIX,
102
109
  gate_stamp,
@@ -47,7 +47,7 @@ const HUMAN_REVIEW = 'human-review'
47
47
  // test. `LIVE` is the silent norm; `STOPPED` and `UNKNOWN` lead the difference lines.
48
48
  const LIVE = 'live'
49
49
  const STOPPED = 'stopped'
50
- // The verdicts `epic --reconcile` prints; kept in step with the `git_epic_reconcile` constants in its
50
+ // The verdicts `epic --reconcile` prints; kept in step with the `epic_reconcile` constants in its
51
51
  // test.
52
52
  const RECONCILED = 'reconciled'
53
53
  const NOTHING_TO_RECONCILE = 'nothing to reconcile'
@@ -302,7 +302,7 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
302
302
  decision: 'Whether cross-file or cross-repository code duplication exists',
303
303
  args: '',
304
304
  vocabulary: ['clean', 'clones:'],
305
- single_source: 'prompts/collaboration-workflow/no-clones.md',
305
+ single_source: 'prompts/collaboration-workflow/principles.md → クローン禁止',
306
306
  },
307
307
  {
308
308
  name: 'epic:reconcile',
@@ -50,7 +50,7 @@ function is_impl_cut_segment(segment: string): boolean {
50
50
  }
51
51
 
52
52
  // Whether this command takes the implementation-phase cut, segment-wise so a spelling quoted inside a
53
- // body is not read as the call, and alias-expanded so `pnpm josh rct --impl` is the same call.
53
+ // body is not read as the call.
54
54
  function takes_the_impl_cut(command: string): boolean {
55
55
  return shell_segments.segments_of(command).some((segment) => is_impl_cut_segment(segment))
56
56
  }
@@ -21,8 +21,7 @@ import { tail_commands } from './tail-commands'
21
21
  // "fold first". **No new predicate**: the filing trigger is `bash_triggers.is_issue_filing`, and the
22
22
  // prior-filing count is `filing_cap.prior_filing_count`, both reused verbatim.
23
23
 
24
- // `pnpm josh issue:fold`, the one act the rule asks for. The alias `isf` is expanded where the command
25
- // is read, so the canonical name matches both spellings.
24
+ // `pnpm josh issue:fold`, the one act the rule asks for.
26
25
  const FOLD_NAMES: ReadonlySet<string> = new Set(['issue:fold'])
27
26
 
28
27
  // Keeping the rule: a call that runs the fold. Segment-wise, so a spelling quoted inside a filing's
@@ -29,8 +29,7 @@ import { shell_segments } from './shell-segments'
29
29
 
30
30
  // The parent backlogrun budget/ownership commands. A child never runs either: `run:carry` begins and
31
31
  // advances the run budget the parent owns, and `run:merge` counts a returned child into it. Named in
32
- // canonical form only — `shell_segments.is_josh_command` canonicalizes an alias (`rc`, `rmg`) before
33
- // the match, so `pnpm josh rmg` arrives as `run:merge`.
32
+ // canonical form only — `shell_segments.is_josh_command` canonicalizes an alias before the match.
34
33
  const RUN_BUDGET_COMMANDS: ReadonlySet<string> = new Set(['run:merge', 'run:carry'])
35
34
 
36
35
  // A segment that invokes one of the parent's budget commands. Segment-wise and alias-expanded for the
@@ -30,8 +30,7 @@ import { shell_segments } from './shell-segments'
30
30
  // the stop, the reason is a checklist rather than an accusation, and a child that already parked simply
31
31
  // reissues. One wasted round trip on the compliant path is the safe direction; a missed park is not.
32
32
 
33
- // `josh notify`, in either spelling — the alias `nf` is expanded to `notify` before this sees it, the
34
- // same way `pre-gate-cut.ts` matches `gate` through `ga`.
33
+ // `josh notify`, named canonically — `shell_segments.is_josh_command` expands an alias before the match.
35
34
  const NOTIFY_COMMANDS: ReadonlySet<string> = new Set(['notify'])
36
35
  // The task type that marks a stop waiting on a person, taken quoted or bare so `--task-type
37
36
  // confirmation` and `--task-type=confirmation` read alike. A `progress` or `pr` notification is not a
@@ -44,8 +44,8 @@ const HOLD_COMMANDS: ReadonlySet<string> = new Set(['run:hold'])
44
44
  // predicate exists to keep it out of.
45
45
  const HOLD_MODE_FLAG = /(?:^|\s)--release(?:[=\s]|$)/u
46
46
 
47
- // **Alias-expanded rather than matched as text**, so `pnpm josh ga` and `pnpm josh rct` are the same
48
- // calls as their canonical spellings. Segment-wise for the reason every other row here is: one shell
47
+ // **Alias-expanded rather than matched as text**, so `pnpm josh ga` is the same call as `pnpm josh
48
+ // gate`. Segment-wise for the reason every other row here is: one shell
49
49
  // line carries several commands, and a name quoted inside a body is not the command being invoked.
50
50
  //
51
51
  // **One reading of that question rather than one per name set.** Four predicates here differ only in
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
1
  import { json_value } from '#scripts/lib/json-value'
2
+ import { file_reader } from '#scripts/lib/read-file'
3
3
  import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
4
4
  import { bash_triggers } from './bash-triggers'
5
5
  import { shell_segments } from './shell-segments'
@@ -81,7 +81,7 @@ function string_field(input: Record<string, unknown>, key: string): string {
81
81
  // measure against. Injected so the suite controls it without touching the real filesystem, the way
82
82
  // `file-body.ts` injects its existence check. A new file reads as empty, so its whole body is added.
83
83
  function read_existing(file_path: string): string {
84
- return existsSync(file_path) ? readFileSync(file_path, 'utf8') : ''
84
+ return file_reader.read_file_or_empty(file_path)
85
85
  }
86
86
 
87
87
  function texts_of(
@@ -0,0 +1,99 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { mkdirSync, renameSync, statSync, writeFileSync } from 'node:fs'
3
+ import path from 'node:path'
4
+ import { file_reader } from '#scripts/lib/read-file'
5
+
6
+ // The `rule:value` reading at the `backlogrun` loop head, without the loop waiting on it
7
+ // (joshuafolkken/kit#2881). Measuring re-reads every transcript and takes minutes, and `backlog:offer`
8
+ // ran it synchronously on every iteration, so each dispatch waited on it. The loop head now prints the
9
+ // last reading from a cache and, when the last refresh is old enough, starts one in a detached process
10
+ // — `josh rule:value --refresh` — so the next iteration prints the newer reading.
11
+ //
12
+ // **It reports and never fails**, as `rule:value` itself does: a missing cache, an unwritable directory
13
+ // or a refresh that could not start prints at most nothing, and never changes the offer's answer.
14
+
15
+ const CACHE_DIRECTORY = ['node_modules', '.cache', 'josh']
16
+ const CACHE_FILE = 'rule-value.txt'
17
+ // Touched when a refresh starts, so the loop head starts at most one per interval however often it runs.
18
+ const STAMP_FILE = 'rule-value.stamp'
19
+ const MS_PER_MINUTE = 60_000
20
+ const REFRESH_MINUTES = 15
21
+ const REFRESH_MS = REFRESH_MINUTES * MS_PER_MINUTE
22
+ const REFRESH_ARGV = ['josh', 'rule:value', '--refresh']
23
+ const PNPM = 'pnpm'
24
+ const TEMPORARY_SUFFIX = '.tmp'
25
+
26
+ interface CachePorts {
27
+ now: () => number
28
+ start_refresh: (cwd: string) => void
29
+ }
30
+
31
+ function cache_path(cwd: string, file: string): string {
32
+ return path.join(cwd, ...CACHE_DIRECTORY, file)
33
+ }
34
+
35
+ function read_cached(cwd: string): string | undefined {
36
+ return file_reader.read_if_readable(cache_path(cwd, CACHE_FILE))
37
+ }
38
+
39
+ function is_refresh_due(cwd: string, now_ms: number): boolean {
40
+ try {
41
+ return now_ms - statSync(cache_path(cwd, STAMP_FILE)).mtimeMs >= REFRESH_MS
42
+ } catch {
43
+ return true
44
+ }
45
+ }
46
+
47
+ function write_cache_file(cwd: string, file: string, text: string): void {
48
+ const target = cache_path(cwd, file)
49
+ const temporary = `${target}${TEMPORARY_SUFFIX}`
50
+
51
+ mkdirSync(path.dirname(target), { recursive: true })
52
+ writeFileSync(temporary, text)
53
+ renameSync(temporary, target)
54
+ }
55
+
56
+ function ignore_spawn_error(): void {
57
+ /* a refresh that could not start leaves the last reading in place */
58
+ }
59
+
60
+ // Detached with no descriptor shared, so the loop head exits — and a backgrounded caller completes —
61
+ // without waiting on the measurement.
62
+ function start_refresh_default(cwd: string): void {
63
+ const child = spawn(PNPM, REFRESH_ARGV, { cwd, detached: true, stdio: 'ignore' })
64
+
65
+ child.on('error', ignore_spawn_error)
66
+ child.unref()
67
+ }
68
+
69
+ const DEFAULT_PORTS: CachePorts = { now: Date.now, start_refresh: start_refresh_default }
70
+
71
+ function refresh_if_due(cwd: string, ports: CachePorts): void {
72
+ if (!is_refresh_due(cwd, ports.now())) return
73
+
74
+ write_cache_file(cwd, STAMP_FILE, new Date(ports.now()).toISOString())
75
+ ports.start_refresh(cwd)
76
+ }
77
+
78
+ // The loop-head call: the last reading to stderr, then a refresh when one is due.
79
+ function emit(cwd: string = process.cwd(), ports: CachePorts = DEFAULT_PORTS): void {
80
+ const cached = read_cached(cwd)
81
+
82
+ if (cached !== undefined) process.stderr.write(cached)
83
+
84
+ try {
85
+ refresh_if_due(cwd, ports)
86
+ } catch {
87
+ // A refresh that could not be recorded or started leaves the last reading in place.
88
+ }
89
+ }
90
+
91
+ // The detached refresh's body: the reading written whole, so a reader never sees half of one.
92
+ function store(text: string, cwd: string = process.cwd()): void {
93
+ write_cache_file(cwd, CACHE_FILE, `${text}\n`)
94
+ }
95
+
96
+ const rule_value_cache = { CACHE_FILE, REFRESH_MS, STAMP_FILE, cache_path, emit, store }
97
+
98
+ export type { CachePorts }
99
+ export { rule_value_cache }
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
3
3
  import { cost_transcript, type SessionFile } from '#scripts/cost-runtime/cost-transcript'
4
4
  import { delivered_rules } from './delivered-rules'
5
5
  import { rule_value, type RuleReading } from './rule-value'
6
+ import { rule_value_cache } from './rule-value-cache'
6
7
 
7
8
  // `josh rule:value` — what each delivered rule earns on the channel that carries it, one row per rule
8
9
  // (joshuafolkken/kit#2271). `rule_value.measure` was public but had no caller outside its own test, so
@@ -14,6 +15,8 @@ import { rule_value, type RuleReading } from './rule-value'
14
15
  // no measurable transcript prints `NO_TARGETS` and exits zero rather than erroring, so a fresh
15
16
  // checkout reads differently from a broken command.
16
17
 
18
+ const ARGV_OFFSET = 2
19
+ const REFRESH_FLAG = '--refresh'
17
20
  const PERCENT_SIGN = '%'
18
21
  const COLUMN_GAP = ' '
19
22
  const NO_TARGETS = 'no measurement targets'
@@ -98,24 +101,22 @@ function render(runs: ReadonlyArray<ReadonlyArray<string>>): string {
98
101
  .join('\n')
99
102
  }
100
103
 
101
- // The loop-head call: `backlog:offer` writes this to stderr once per iteration, so a rule that never
102
- // fires appears as a printed row rather than as something a person has to go looking for.
103
- function emit(cwd: string = process.cwd()): void {
104
- process.stderr.write(`${render(gather_runs(cwd))}\n`)
105
- }
104
+ // `--refresh` is the detached measurement the `backlogrun` loop head starts: the reading goes to the
105
+ // cache `backlog:offer` prints from, never to stdout (`rule-value-cache.ts`, joshuafolkken/kit#2881).
106
+ function run_rule_value(argv: ReadonlyArray<string> = [], cwd: string = process.cwd()): number {
107
+ const reading = render(gather_runs(cwd))
106
108
 
107
- function run_rule_value(cwd: string = process.cwd()): number {
108
- process.stdout.write(`${render(gather_runs(cwd))}\n`)
109
+ if (argv.includes(REFRESH_FLAG)) rule_value_cache.store(reading, cwd)
110
+ else process.stdout.write(`${reading}\n`)
109
111
 
110
112
  return 0
111
113
  }
112
114
 
113
115
  if (process.argv[1] === fileURLToPath(import.meta.url)) {
114
- process.exitCode = run_rule_value()
116
+ process.exitCode = run_rule_value(process.argv.slice(ARGV_OFFSET))
115
117
  }
116
118
 
117
119
  const rule_value_cli = {
118
- emit,
119
120
  gather_runs,
120
121
  group_by_run,
121
122
  render,
@@ -9,7 +9,7 @@ import { delivered_rules, type MeasuredRule } from './delivered-rules'
9
9
  // (joshuafolkken/kit#1525).
10
10
  //
11
11
  // **It exists because the eviction order was decided by protection rather than by value.**
12
- // `.claude/skills/workflow-commands/rule-residency.md` recorded that when a rule is trimmed to keep a
12
+ // `prompts/collaboration-workflow/residency.md` records that when a rule is trimmed to keep a
13
13
  // byte count, the sentence that goes is the one no marker pinned (joshuafolkken/kit#951) — so the
14
14
  // least-defended text leaves, never the least-useful. Nothing in the repository could tell the two
15
15
  // apart: `scripts/josh/hook-decision.ts` keeps a once-per-run stamp that answers "has this fired
@@ -1,4 +1,4 @@
1
- import { parseArgs } from 'node:util'
1
+ import { cli_flags } from '#scripts/lib/cli-flags'
2
2
  import { run_carry, type CarryChange, type CarryClaimRequest, type CarryOwner } from './run-carry'
3
3
  import { run_issue_number } from './run-issue-number'
4
4
 
@@ -76,11 +76,7 @@ type Request =
76
76
  const READ_REQUEST: Request = { kind: 'read' }
77
77
 
78
78
  function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
79
- try {
80
- return parseArgs({ args: [...argv], options: OPTIONS, strict: true }).values
81
- } catch {
82
- return undefined
83
- }
79
+ return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values
84
80
  }
85
81
 
86
82
  function text_of(value: OptionValue): string | undefined {
@@ -10,7 +10,6 @@ import {
10
10
  type RunCarry,
11
11
  } from './run-carry'
12
12
  import { run_carry_args, type CountRequest, type Request } from './run-carry-args'
13
- import { run_carry_flush } from './run-carry-flush'
14
13
  import { run_carry_stash } from './run-carry-stash'
15
14
  import { run_event_stream } from './run-event-stream'
16
15
  import { run_event_stream_emit } from './run-event-stream-emit'
@@ -344,16 +343,15 @@ async function count(
344
343
  // **A stop over a record that is still there pushes one ⏸️ confirmation before the record goes**
345
344
  // (joshuafolkken/kit#2136). `plan` is read from the record `--end` is about to remove, so the second
346
345
  // `--end` reads `none` and plans nothing — one stop never notifies twice. A clean `--end` passes no
347
- // reason and stays silent, because a completed run has its own notification. The batch's one ledger
348
- // flush rides the same read for the same reason (joshuafolkken/kit#2492, `run-carry-flush.ts`), and
349
- // runs last: it waits out a pull request's CI, so a flush cut short must find the record already gone
350
- // and the stop already announced. The closed-issue stash report (joshuafolkken/kit#2505,
351
- // `run-carry-stash.ts`) is once per invocation on the same read, and goes first because it is quick.
346
+ // reason and stays silent, because a completed run has its own notification. The closed-issue stash
347
+ // report (joshuafolkken/kit#2505, `run-carry-stash.ts`) is once per invocation on the same read.
348
+ //
349
+ // **No ledger flush rides it any more** (joshuafolkken/kit#2919): each lane merges its own ledger
350
+ // lines with its own pull request, so there is nothing left in the primary checkout to flush.
352
351
  async function report_end(read: CarryRead): Promise<void> {
353
352
  if (read.kind === 'none') return
354
353
 
355
354
  await run_carry_stash.report_orphans()
356
- await run_carry_flush.flush_ledger()
357
355
  }
358
356
 
359
357
  async function finish(
@@ -1,4 +1,4 @@
1
- import { parseArgs } from 'node:util'
1
+ import { cli_flags } from '#scripts/lib/cli-flags'
2
2
  import { run_issue_number } from './run-issue-number'
3
3
 
4
4
  // `josh run:cut` turns its `argv` into one of four requests. The contract mirrors `run-carry-args.ts`:
@@ -47,18 +47,12 @@ type Request =
47
47
  | { kind: 'end' }
48
48
 
49
49
  function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
50
- try {
51
- const parsed = parseArgs({
52
- args: [...argv],
53
- options: OPTIONS,
54
- strict: true,
55
- allowPositionals: true,
56
- })
57
-
58
- return { values: parsed.values, positionals: parsed.positionals }
59
- } catch {
60
- return undefined
61
- }
50
+ return cli_flags.parse_or_undefined({
51
+ args: [...argv],
52
+ options: OPTIONS,
53
+ strict: true,
54
+ allowPositionals: true,
55
+ })
62
56
  }
63
57
 
64
58
  // The issue is the sole positional, and it must be a real issue number: a cut relaunches
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { parseArgs } from 'node:util'
3
+ import { cli_flags } from '#scripts/lib/cli-flags'
4
+ import { error_text } from '#scripts/lib/error-message'
4
5
  import {
5
6
  run_ending,
6
7
  UNREADABLE_VERDICT,
@@ -56,11 +57,7 @@ interface ParsedArguments {
56
57
  }
57
58
 
58
59
  function read_arguments(argv: ReadonlyArray<string>): ParsedArguments | undefined {
59
- try {
60
- return parseArgs({ args: [...argv], options: OPTIONS, allowPositionals: true })
61
- } catch {
62
- return undefined
63
- }
60
+ return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, allowPositionals: true })
64
61
  }
65
62
 
66
63
  function is_valid(parsed: ParsedArguments): boolean {
@@ -96,7 +93,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
96
93
  try {
97
94
  return report(await run_ending.check(request))
98
95
  } catch (error) {
99
- return report_unreadable(error instanceof Error ? error.message : String(error))
96
+ return report_unreadable(error_text.message_of(error))
100
97
  }
101
98
  }
102
99
 
@@ -1,4 +1,5 @@
1
1
  import { stamp_file } from '#scripts/josh/stamp-file'
2
+ import { json_value } from '#scripts/lib/json-value'
2
3
  import { z } from 'zod'
3
4
 
4
5
  // The run's append-only, ordered event stream — the surface a reader who was away rebuilds the run's
@@ -143,11 +144,7 @@ function is_present(event: RunEvent | undefined): event is RunEvent {
143
144
  // A malformed line reads as no event rather than throwing, so one truncated append — a write that lost a
144
145
  // race — never makes the whole stream unreadable.
145
146
  function parse_line(line: string): RunEvent | undefined {
146
- try {
147
- return event_schema.safeParse(JSON.parse(line)).data
148
- } catch {
149
- return undefined
150
- }
147
+ return event_schema.safeParse(json_value.parse_or_undefined(line)).data
151
148
  }
152
149
 
153
150
  // Every event on the stream, oldest first. An absent or unreadable file is an empty stream, the same
@@ -73,10 +73,27 @@ function is_owned_here(carry: RunCarry, ancestry: () => ReadonlySet<number>): bo
73
73
  return carry.owner_pid !== undefined && ancestry().has(carry.owner_pid)
74
74
  }
75
75
 
76
- function is_driven_here(read: CarryRead | undefined, ancestry: () => ReadonlySet<number>): boolean {
77
- if (read?.kind !== 'carried' || !is_driving(read)) return false
76
+ function driven_here(
77
+ read: CarryRead | undefined,
78
+ ancestry: () => ReadonlySet<number>,
79
+ ): RunCarry | undefined {
80
+ if (read?.kind !== 'carried' || !is_driving(read)) return undefined
78
81
 
79
- return is_owned_here(read.carry, ancestry)
82
+ return is_owned_here(read.carry, ancestry) ? read.carry : undefined
83
+ }
84
+
85
+ /**
86
+ * The live, un-handed-off carry record this session drives as the `backlogrun` parent, or `undefined`
87
+ * for a lane child or any session that does not own one. Returned rather than tested so a caller that
88
+ * also needs the record's counters (the parent cut's cap, joshuafolkken/kit#2947) reads it once.
89
+ */
90
+ async function driving_carry(
91
+ source: EnvironmentSource = process.env,
92
+ ancestry: () => ReadonlySet<number> = lane_reap.own_ancestry,
93
+ ): Promise<RunCarry | undefined> {
94
+ if (lane_child_marker.marked_issue(source) !== undefined) return undefined
95
+
96
+ return driven_here(await read_carry_here(), ancestry)
80
97
  }
81
98
 
82
99
  /**
@@ -88,14 +105,13 @@ async function is_backlog_parent(
88
105
  source: EnvironmentSource = process.env,
89
106
  ancestry: () => ReadonlySet<number> = lane_reap.own_ancestry,
90
107
  ): Promise<boolean> {
91
- if (lane_child_marker.marked_issue(source) !== undefined) return false
92
-
93
- return is_driven_here(await read_carry_here(), ancestry)
108
+ return (await driving_carry(source, ancestry)) !== undefined
94
109
  }
95
110
 
96
111
  const run_headless = {
97
112
  HEADLESS_ENV_KEY,
98
113
  current_carry,
114
+ driving_carry,
99
115
  environment,
100
116
  is_backlog_parent,
101
117
  is_headless,
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { fileURLToPath } from 'node:url'
3
- import { parseArgs } from 'node:util'
3
+ import { cli_flags } from '#scripts/lib/cli-flags'
4
+ import { error_text } from '#scripts/lib/error-message'
4
5
  import { run_issue_number } from './run-issue-number'
5
6
  import {
6
7
  MS_PER_MINUTE,
@@ -89,11 +90,7 @@ interface ParsedArguments {
89
90
  }
90
91
 
91
92
  function read_arguments(argv: ReadonlyArray<string>): ParsedArguments | undefined {
92
- try {
93
- return parseArgs({ args: [...argv], options: OPTIONS, allowPositionals: true })
94
- } catch {
95
- return undefined
96
- }
93
+ return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, allowPositionals: true })
97
94
  }
98
95
 
99
96
  function is_valid_target(parsed: ParsedArguments): boolean {
@@ -154,7 +151,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
154
151
  try {
155
152
  return report(await run_liveness.check(request))
156
153
  } catch (error) {
157
- return report_undetermined(error instanceof Error ? error.message : String(error))
154
+ return report_undetermined(error_text.message_of(error))
158
155
  }
159
156
  }
160
157