@joshuafolkken/kit 1.1387.0 → 1.1453.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 (278) hide show
  1. package/.claude/skills/epic-commands/SKILL.md +1 -1
  2. package/.claude/skills/epic-commands/epic-bundle.md +5 -0
  3. package/.claude/skills/workflow-commands/backlogrun-lanes.md +5 -3
  4. package/.claude/skills/workflow-commands/backlogrun-park.md +2 -0
  5. package/.claude/skills/workflow-commands/backlogrun-steps.md +13 -10
  6. package/.claude/skills/workflow-commands/backlogrun.md +3 -2
  7. package/.claude/skills/workflow-commands/chain-rule.md +4 -2
  8. package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
  9. package/.claude/skills/workflow-commands/halfrun.md +1 -1
  10. package/.claude/skills/workflow-commands/issue-comments.md +6 -0
  11. package/.claude/skills/workflow-commands/kickoff.md +12 -13
  12. package/.claude/skills/workflow-commands/observation-filing.md +1 -1
  13. package/.claude/skills/workflow-commands/observation-ledger.md +3 -2
  14. package/.claude/skills/workflow-commands/pre-gate-cut.md +5 -3
  15. package/.claude/skills/workflow-commands/prerequisite.md +6 -0
  16. package/.claude/skills/workflow-commands/retrospective.md +5 -5
  17. package/.claude/skills/workflow-commands/split-assessment.md +2 -2
  18. package/README.md +27 -4
  19. package/dist/hooks/audit-provision.js +2 -2
  20. package/dist/hooks/batch-guard.js +7 -7
  21. package/dist/hooks/{chunk-2YSW3CXH.js → chunk-3C4XRYG6.js} +35 -11
  22. package/dist/hooks/{chunk-GJ7JPIHN.js → chunk-5C7L6FRC.js} +2704 -190
  23. package/dist/hooks/{chunk-FZ6FUSBM.js → chunk-723HKZO5.js} +1 -1
  24. package/dist/hooks/{chunk-STDSEPWU.js → chunk-D3NJXN5M.js} +4 -4
  25. package/dist/hooks/{chunk-NEC4AT4Z.js → chunk-DGT54IH5.js} +4 -0
  26. package/dist/hooks/{chunk-LMWBRJIP.js → chunk-EF5GQ6WS.js} +3 -3
  27. package/dist/hooks/{chunk-JBBMAPC4.js → chunk-IX6TTTI6.js} +4 -0
  28. package/dist/hooks/{chunk-IYVOLN6A.js → chunk-LBFYEOXJ.js} +3 -3
  29. package/dist/hooks/{chunk-SL4GQ2XP.js → chunk-MBJYQPEK.js} +106 -23
  30. package/dist/hooks/{chunk-WNFJLBXI.js → chunk-NO4AALP5.js} +3 -3
  31. package/dist/hooks/{chunk-J3KSS7II.js → chunk-OLMO72ON.js} +1 -1
  32. package/dist/hooks/{chunk-DCY2GWHC.js → chunk-SHYUQOIS.js} +5 -5
  33. package/dist/hooks/{chunk-EAQEGME7.js → chunk-UT7YC3N2.js} +1 -1
  34. package/dist/hooks/{chunk-EBSHN25W.js → chunk-W5ZKGTSY.js} +4 -4
  35. package/dist/hooks/{chunk-GNDJ7NJ7.js → chunk-Y3GKIKGW.js} +209 -56
  36. package/dist/hooks/{chunk-U32VSRNG.js → chunk-Z4XI7CZS.js} +89 -34
  37. package/dist/hooks/codex-hook-adapter.js +15 -15
  38. package/dist/hooks/duplicate-read-guard.js +8 -8
  39. package/dist/hooks/format-edited.js +5 -5
  40. package/dist/hooks/inputs.json +261 -201
  41. package/dist/hooks/investigation-guard.js +8 -8
  42. package/dist/hooks/pretool-guard.js +14 -14
  43. package/dist/hooks/session-lang.js +69 -16
  44. package/dist/hooks/stop-guard.js +13 -38
  45. package/dist/josh.js +95 -19
  46. package/package.json +4 -3
  47. package/prompts/collaboration-workflow/issue-template.md +10 -8
  48. package/prompts/review.md +30 -4
  49. package/scripts/agent/claude-agent-argv.ts +13 -1
  50. package/scripts/auth/auth-setup-cli.ts +101 -0
  51. package/scripts/auth/claude-token-output.ts +78 -0
  52. package/scripts/auth/claude-token.ts +68 -0
  53. package/scripts/auto-ok/auto-ok-judge-cli.ts +125 -0
  54. package/scripts/auto-ok/auto-ok-judge-read.ts +95 -0
  55. package/scripts/auto-ok/auto-ok-judge-write.ts +109 -0
  56. package/scripts/auto-ok/auto-ok-judge.ts +67 -0
  57. package/scripts/backlog/backlog-drive-cli.ts +14 -0
  58. package/scripts/backlog/backlog-drive-epic.ts +24 -8
  59. package/scripts/backlog/backlog-drive-launch.ts +3 -0
  60. package/scripts/backlog/backlog-drive-named-offer.ts +7 -2
  61. package/scripts/backlog/backlog-drive-named.ts +4 -2
  62. package/scripts/backlog/backlog-drive-unread.ts +56 -0
  63. package/scripts/backlog/backlog-drive.ts +38 -11
  64. package/scripts/backlog/backlog-overlap.ts +22 -5
  65. package/scripts/backlog/backlog-ready.ts +1 -3
  66. package/scripts/backlog/backlogrun-args.ts +13 -2
  67. package/scripts/backlog/backlogrun-cli.ts +91 -19
  68. package/scripts/backlog/backlogrun-launch.ts +24 -0
  69. package/scripts/backlog/backlogrun-replace.ts +166 -0
  70. package/scripts/backlog/backlogrun-stop.ts +178 -0
  71. package/scripts/behavior/behavior-cli.ts +6 -1
  72. package/scripts/doctor/doctor-io.ts +60 -9
  73. package/scripts/doctor/doctor.ts +20 -1
  74. package/scripts/document/document-byte-budget.ts +3 -3
  75. package/scripts/epic/epic-bundle-cli.ts +8 -8
  76. package/scripts/epic/epic-bundle-referenced.ts +2 -5
  77. package/scripts/epic/epic-bundle.ts +39 -4
  78. package/scripts/exports/unused-members-cli.ts +2 -1
  79. package/scripts/followup/followup-checks-event.ts +82 -0
  80. package/scripts/followup/git-followup-finish.ts +16 -2
  81. package/scripts/followup/git-followup-workflow.ts +5 -2
  82. package/scripts/followup/git-pr-followup-checks.ts +18 -4
  83. package/scripts/followup/git-pr-followup-wrapup.ts +5 -0
  84. package/scripts/followup/git-pr-followup.ts +4 -1
  85. package/scripts/fresh-machine/fresh-machine-cli.ts +204 -0
  86. package/scripts/fresh-machine/fresh-machine-docker.ts +65 -0
  87. package/scripts/fresh-machine/fresh-machine-image.ts +62 -0
  88. package/scripts/fresh-machine/fresh-machine-log.ts +61 -0
  89. package/scripts/fresh-machine/fresh-machine-patterns.ts +175 -0
  90. package/scripts/fresh-machine/fresh-machine-run.ts +215 -0
  91. package/scripts/fresh-machine/fresh-machine-script.ts +45 -0
  92. package/scripts/fresh-machine/fresh-machine-spec.ts +57 -0
  93. package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +34 -0
  94. package/scripts/fresh-machine/specs/kit-test-backlogrun.json +31 -0
  95. package/scripts/gate/gate-report.ts +70 -15
  96. package/scripts/gate/project-checks.ts +18 -4
  97. package/scripts/gate/scoped-green.ts +2 -1
  98. package/scripts/gate/verification-gate.ts +4 -3
  99. package/scripts/gh/gh-author-association.ts +14 -0
  100. package/scripts/gh/git-gh-conditional.ts +66 -0
  101. package/scripts/gh/git-gh-exec.ts +1 -1
  102. package/scripts/gh/git-gh-issue-read.ts +6 -3
  103. package/scripts/gh/git-gh-issue-rest.ts +1 -0
  104. package/scripts/gh/git-gh-issue-write.ts +5 -1
  105. package/scripts/gh/git-gh-issue.ts +13 -1
  106. package/scripts/gh/git-pr-checks-eval.ts +13 -0
  107. package/scripts/gh/git-pr-checks-parse.ts +8 -1
  108. package/scripts/gh/git-pr-checks-watch.ts +11 -4
  109. package/scripts/gh/git-pr-checks.ts +36 -2
  110. package/scripts/git/git-command.ts +16 -4
  111. package/scripts/git/git-schemas.ts +1 -0
  112. package/scripts/git/git-spawn-sync.ts +1 -1
  113. package/scripts/hooks/pretool-guard.ts +3 -1
  114. package/scripts/init/hook-command-rewrite.ts +31 -0
  115. package/scripts/init/init-actions.ts +7 -4
  116. package/scripts/init/init-ai-copy.ts +34 -11
  117. package/scripts/init/init-basic.ts +9 -4
  118. package/scripts/init/init-bootstrap.ts +6 -2
  119. package/scripts/init/init-dry-run-report.ts +150 -0
  120. package/scripts/init/init-dry-run.ts +189 -0
  121. package/scripts/init/init-install.ts +8 -1
  122. package/scripts/init/init-logic-toolchain.ts +76 -0
  123. package/scripts/init/init-logic.ts +2 -0
  124. package/scripts/init/init.ts +62 -42
  125. package/scripts/init/kit-setup-state.ts +11 -17
  126. package/scripts/init/own-formatter.ts +21 -0
  127. package/scripts/init/project-profile.ts +60 -12
  128. package/scripts/init/start-dry-run.ts +70 -0
  129. package/scripts/init/start-exec.ts +27 -1
  130. package/scripts/init/start-plan.ts +23 -3
  131. package/scripts/init/start-setup-pr.ts +13 -24
  132. package/scripts/init/start-steps.ts +62 -1
  133. package/scripts/init/start.ts +38 -17
  134. package/scripts/init/workflow-mark.ts +35 -0
  135. package/scripts/init/working-tree.ts +116 -0
  136. package/scripts/issue/issue-ack-cli.ts +84 -0
  137. package/scripts/issue/issue-auto-ok.ts +68 -99
  138. package/scripts/issue/issue-file-cli.ts +17 -17
  139. package/scripts/issue/issue-file.ts +1 -6
  140. package/scripts/issue/issue-labels.ts +46 -10
  141. package/scripts/issue/issue-release-cli.ts +69 -23
  142. package/scripts/issue/issue-release.ts +3 -1
  143. package/scripts/issue/issue-state-cli.ts +14 -4
  144. package/scripts/issue/issue-watch-diff.ts +152 -0
  145. package/scripts/issue/issue-watch-gate.ts +81 -0
  146. package/scripts/issue/issue-watch-hook.ts +54 -0
  147. package/scripts/issue/issue-watch.ts +240 -0
  148. package/scripts/issue/measure-rerun-cli.ts +3 -4
  149. package/scripts/josh/josh-command-map.ts +4 -0
  150. package/scripts/josh/josh-commands-ai.ts +11 -0
  151. package/scripts/josh/josh-commands-backlog.ts +2 -2
  152. package/scripts/josh/josh-commands-development.ts +1 -1
  153. package/scripts/josh/josh-commands-issue.ts +10 -3
  154. package/scripts/josh/josh-commands-lane.ts +11 -2
  155. package/scripts/josh/josh-commands-project.ts +26 -2
  156. package/scripts/josh/josh-verdict.ts +33 -4
  157. package/scripts/josh/process-identity.ts +32 -6
  158. package/scripts/lane/claude-lane-broker.ts +142 -0
  159. package/scripts/lane/lane-await.ts +6 -2
  160. package/scripts/lane/lane-broker-server.ts +69 -0
  161. package/scripts/lane/lane-cache.ts +37 -7
  162. package/scripts/lane/lane-capacity.ts +8 -6
  163. package/scripts/lane/lane-child-invocation.ts +1 -0
  164. package/scripts/lane/lane-cli.ts +15 -1
  165. package/scripts/lane/lane-close.ts +15 -6
  166. package/scripts/lane/lane-dispatch.ts +36 -10
  167. package/scripts/lane/lane-environment.ts +1 -1
  168. package/scripts/lane/lane-launch-cli.ts +13 -4
  169. package/scripts/lane/lane-ledger.ts +21 -3
  170. package/scripts/lane/lane-measure-cli.ts +10 -2
  171. package/scripts/lane/lane-open.ts +9 -1
  172. package/scripts/lane/lane-preserve.ts +49 -0
  173. package/scripts/lane/lane-reap.ts +20 -5
  174. package/scripts/lane/lane-registry.ts +6 -0
  175. package/scripts/lane/lane-restart-cli.ts +51 -0
  176. package/scripts/lane/lane-restart.ts +175 -0
  177. package/scripts/lane/lane-session-broker.ts +363 -0
  178. package/scripts/lane/lane-spent.ts +195 -0
  179. package/scripts/lane/lane-stage-stats.ts +27 -3
  180. package/scripts/lane/lane-stop-stats.ts +96 -0
  181. package/scripts/lane/openai-lane-supervisor.ts +2 -2
  182. package/scripts/lane/process-tools.ts +31 -0
  183. package/scripts/lib/buffered-process.ts +36 -2
  184. package/scripts/lib/pnpm-reach.ts +27 -0
  185. package/scripts/lib/status-icons.ts +5 -1
  186. package/scripts/lint/lint-parallel.ts +19 -2
  187. package/scripts/observations/observation-ledger-home.ts +1 -0
  188. package/scripts/observations/observation-ledger-migrate.ts +3 -15
  189. package/scripts/release/release-issue-close.ts +133 -0
  190. package/scripts/release/release-publish.ts +12 -1
  191. package/scripts/repo/repository-labels.ts +11 -1
  192. package/scripts/review/comment-ranges.ts +199 -0
  193. package/scripts/review/review-attest-cli.ts +21 -6
  194. package/scripts/review/review-brief-cli.ts +24 -7
  195. package/scripts/review/review-code-tree.ts +119 -0
  196. package/scripts/review/review-comment-round.ts +114 -0
  197. package/scripts/review/review-record-cli.ts +39 -17
  198. package/scripts/review/review-round2-cli.ts +16 -5
  199. package/scripts/review/review-round2.ts +30 -10
  200. package/scripts/review/review-stamps.ts +22 -1
  201. package/scripts/rules/implementation-cut.ts +3 -3
  202. package/scripts/rules/pre-gate-cut.ts +1 -1
  203. package/scripts/run/board/run-board-checks.ts +42 -0
  204. package/scripts/run/board/run-board-cli.ts +2 -0
  205. package/scripts/run/board/run-board-every.ts +3 -1
  206. package/scripts/run/board/run-board-fit.ts +16 -5
  207. package/scripts/run/board/run-board-fresh.ts +64 -0
  208. package/scripts/run/board/run-board-header.ts +129 -53
  209. package/scripts/run/board/run-board-labels.ts +47 -4
  210. package/scripts/run/board/run-board-lane-event.ts +66 -0
  211. package/scripts/run/board/run-board-layout.ts +57 -9
  212. package/scripts/run/board/run-board-now.ts +234 -0
  213. package/scripts/run/board/run-board-operation-read.ts +75 -0
  214. package/scripts/run/board/run-board-operation.ts +150 -0
  215. package/scripts/run/board/run-board-phase.ts +26 -1
  216. package/scripts/run/board/run-board-plan.ts +4 -6
  217. package/scripts/run/board/run-board-read.ts +49 -3
  218. package/scripts/run/board/run-board-reason.ts +124 -0
  219. package/scripts/run/board/run-board-render-checks.ts +55 -0
  220. package/scripts/run/board/run-board-render-legend.ts +12 -3
  221. package/scripts/run/board/run-board-render-slot.ts +2 -2
  222. package/scripts/run/board/run-board-render-tail.ts +128 -0
  223. package/scripts/run/board/run-board-render-thanks.ts +50 -0
  224. package/scripts/run/board/run-board-render-title.ts +58 -0
  225. package/scripts/run/board/run-board-render.ts +96 -80
  226. package/scripts/run/board/run-board-row-yield.ts +32 -0
  227. package/scripts/run/board/run-board-setup.ts +61 -13
  228. package/scripts/run/board/run-board-state.ts +10 -0
  229. package/scripts/run/board/run-board-status.ts +66 -7
  230. package/scripts/run/board/run-board-thanks-csv.ts +71 -0
  231. package/scripts/run/board/run-board-thanks-read.ts +112 -0
  232. package/scripts/run/board/run-board-thanks.ts +53 -0
  233. package/scripts/run/board/run-board-tick.ts +50 -21
  234. package/scripts/run/board/run-board-usual.ts +44 -0
  235. package/scripts/run/carry/run-carry-args.ts +9 -1
  236. package/scripts/run/carry/run-carry-cli.ts +2 -45
  237. package/scripts/run/carry/run-carry-end.ts +73 -0
  238. package/scripts/run/carry/run-carry.ts +8 -1
  239. package/scripts/run/entry/run-entry-cli.ts +5 -0
  240. package/scripts/run/event/run-event-checks.ts +54 -0
  241. package/scripts/run/event/run-event-render.ts +2 -0
  242. package/scripts/run/event/run-event-scope.ts +1 -0
  243. package/scripts/run/event/run-event-setup.ts +55 -11
  244. package/scripts/run/event/run-event-stream.ts +27 -11
  245. package/scripts/run/merge/run-merge-cli.ts +7 -4
  246. package/scripts/run/merge/run-merge-ship.ts +61 -0
  247. package/scripts/run/merge/run-merge-steps.ts +7 -14
  248. package/scripts/run/merge/run-merge-token.ts +16 -2
  249. package/scripts/run/run-headless.ts +1 -0
  250. package/scripts/run/run-step.ts +2 -0
  251. package/scripts/run/ship/run-ship-cli.ts +22 -2
  252. package/scripts/run/ship/run-ship-return.ts +31 -13
  253. package/scripts/run/ship/run-ship-review-steps.ts +17 -4
  254. package/scripts/run/ship/run-ship-stage.ts +1 -0
  255. package/scripts/run/wake/run-wake-await.ts +130 -0
  256. package/scripts/run/wake/run-wake-cli.ts +60 -71
  257. package/scripts/run/wake/run-wake-describe.ts +34 -2
  258. package/scripts/run/wake/run-wake-driver.ts +11 -4
  259. package/scripts/run/wake/run-wake-session.ts +20 -3
  260. package/scripts/run/wake/run-wake-stop.ts +37 -0
  261. package/scripts/run/wake/run-wake-supervise.ts +44 -0
  262. package/scripts/sync/claude-settings-merge.ts +144 -0
  263. package/scripts/sync/sync-ai-files.ts +28 -3
  264. package/scripts/sync/sync.ts +15 -0
  265. package/scripts/test/project-test-script.ts +73 -0
  266. package/scripts/test/skip-marker.ts +14 -1
  267. package/scripts/test/test-related.ts +3 -2
  268. package/scripts/test/test-unit-guard.ts +78 -14
  269. package/scripts/time-runtime/time-reported-failure.ts +11 -3
  270. package/scripts/uninstall/distributed-files.ts +92 -0
  271. package/scripts/uninstall/uninstall-plan.ts +119 -0
  272. package/scripts/uninstall/uninstall-report.ts +61 -0
  273. package/scripts/uninstall/uninstall.ts +206 -0
  274. package/templates/AGENTS.basic.md +4 -0
  275. package/templates/GEMINI.basic.md +6 -0
  276. package/dist/hooks/chunk-6V5OPIUU.js +0 -13
  277. package/scripts/lane/openai-review-broker.ts +0 -262
  278. package/scripts/run/board/run-board-track.ts +0 -22
@@ -201,6 +201,7 @@ function stop_reason(stage: Stage, conflicts: ReadonlyArray<string> = []): strin
201
201
  }
202
202
 
203
203
  const run_ship_stage = {
204
+ CONFLICT,
204
205
  PHASE,
205
206
  STAGE,
206
207
  clear,
@@ -0,0 +1,130 @@
1
+ import type { AgentProfile } from '#scripts/agent/agent-role-profile'
2
+ import { backlogrun_launch } from '#scripts/backlog/backlogrun-launch'
3
+ import { poll } from '#scripts/lib/poll'
4
+ import { run_carry, type CarryRead, type RunCarry } from '#scripts/run/carry/run-carry'
5
+ import { run_wake, type RunWake } from './run-wake'
6
+ import type { WakeContext } from './run-wake-describe'
7
+
8
+ // How a supervisor claims the run it watches — including the one `josh backlogrun` starts beside the
9
+ // parent it launched, before that parent has claimed the carry record (joshuafolkken/kit#3804).
10
+ //
11
+ // **That supervisor is started by the command, not by the parent's own shell.** Claude Code removes its
12
+ // token from every process its Bash tool starts, so a supervisor the parent started there could not
13
+ // authenticate the sessions it wakes wherever the token is the only credential. Started by the command,
14
+ // it inherits the token — but before the parent has run `run:carry --begin`, so it waits for that record
15
+ // while the launch that will write it is alive, and gives up once that launch is gone.
16
+ //
17
+ // **While a launch is pending, only a live owner's record is its parent's**: a dead owner's record is one
18
+ // an earlier run left, and supervising it would wake that run beside the parent just launched.
19
+
20
+ interface AwaitPorts {
21
+ read_carry: () => CarryRead
22
+ // The invocation of the launched parent that has not claimed the carry record yet, `undefined` once
23
+ // no such launch is alive.
24
+ pending_invocation: () => string | undefined
25
+ is_owner_live: (carry: RunCarry) => boolean
26
+ sleep: (milliseconds: number) => Promise<void>
27
+ }
28
+
29
+ type ClaimResult =
30
+ | { kind: 'claimed'; wake: RunWake; carry: RunCarry }
31
+ | { kind: 'running' }
32
+ | { kind: 'refused'; carry_kind: CarryRead['kind'] }
33
+
34
+ function is_live_carry(read: CarryRead, ports: AwaitPorts): boolean {
35
+ return read.kind === 'carried' && ports.is_owner_live(read.carry)
36
+ }
37
+
38
+ // What the supervisor claims its record for: the live carried run, else the launch about to carry one,
39
+ // else the carried run a supervisor started with no launch pending wakes.
40
+ function supervised_invocation(ports: AwaitPorts): string | undefined {
41
+ const read = ports.read_carry()
42
+
43
+ if (read.kind !== 'carried') return ports.pending_invocation()
44
+ if (ports.is_owner_live(read.carry)) return read.carry.invocation
45
+
46
+ return ports.pending_invocation() ?? read.carry.invocation
47
+ }
48
+
49
+ // **The carry record is read once more after the launch is gone**, because a parent that wrote the
50
+ // record and ended between the two reads still left a run to supervise.
51
+ async function await_carry(ports: AwaitPorts, interval_ms: number): Promise<CarryRead> {
52
+ const read = ports.read_carry()
53
+
54
+ if (is_live_carry(read, ports)) return read
55
+ if (ports.pending_invocation() === undefined) return ports.read_carry()
56
+
57
+ await ports.sleep(interval_ms)
58
+
59
+ return await await_carry(ports, interval_ms)
60
+ }
61
+
62
+ function ports_of(context: WakeContext): AwaitPorts {
63
+ return {
64
+ read_carry: () => run_carry.read_carry(context.carry_target),
65
+ pending_invocation: () => backlogrun_launch.pending_launch(context.git_directory)?.invocation,
66
+ is_owner_live: (carry) => run_carry.is_owner_live(carry),
67
+ sleep: poll.sleep,
68
+ }
69
+ }
70
+
71
+ // The run the claim is held for: the one claimed for, or **a live run carried under another
72
+ // invocation** — a person's own `run:carry --begin` landing before the launched parent's. That run's
73
+ // `run:wake --start` found this supervisor live and answered `running`, so letting the claim go here
74
+ // would leave it with nobody watching it.
75
+ function supervised_carry(
76
+ read: CarryRead,
77
+ invocation: string,
78
+ ports: AwaitPorts,
79
+ ): RunCarry | undefined {
80
+ if (read.kind !== 'carried') return undefined
81
+ if (read.carry.invocation === invocation || ports.is_owner_live(read.carry)) return read.carry
82
+
83
+ return undefined
84
+ }
85
+
86
+ // **The record is re-pointed at the run it now supervises**, because the loop wakes sessions for the
87
+ // record's invocation. **It carries the state a claim for that run would**, not the state the claim
88
+ // inherited for the pending launch's invocation — that run's wakes are not this one's. A refused write
89
+ // means the record is no longer this process's, which the loop's first pass reads as the stop it is.
90
+ function held_for(
91
+ target: string,
92
+ wake: RunWake,
93
+ carry: RunCarry,
94
+ profile: AgentProfile,
95
+ ): ClaimResult {
96
+ if (carry.invocation === wake.invocation) return { kind: 'claimed', wake, carry }
97
+
98
+ const supervised = run_wake.fresh_wake(carry.invocation, new Date(), profile)
99
+
100
+ run_wake.update_wake(target, supervised)
101
+
102
+ return { kind: 'claimed', wake: supervised, carry }
103
+ }
104
+
105
+ // **The record is claimed before the carry record is awaited**, so the parent's own `run:wake --start`
106
+ // finds this supervisor live and answers `running` rather than starting a second one beside it.
107
+ // A parent that ends without carrying a run leaves nothing to supervise, and the claim is let go.
108
+ async function claim_run(
109
+ context: WakeContext,
110
+ profile: AgentProfile,
111
+ interval_ms: number,
112
+ ports: AwaitPorts = ports_of(context),
113
+ ): Promise<ClaimResult> {
114
+ const invocation = supervised_invocation(ports)
115
+ if (invocation === undefined) return { kind: 'refused', carry_kind: ports.read_carry().kind }
116
+ const wake = run_wake.claim(context.wake_target, invocation, new Date(), profile)
117
+ if (wake === undefined) return { kind: 'running' }
118
+ const read = await await_carry(ports, interval_ms)
119
+ const carry = supervised_carry(read, invocation, ports)
120
+ if (carry !== undefined) return held_for(context.wake_target, wake, carry, profile)
121
+
122
+ run_wake.tidy_own_wake(context.wake_target)
123
+
124
+ return { kind: 'refused', carry_kind: read.kind }
125
+ }
126
+
127
+ const run_wake_await = { await_carry, claim_run, supervised_invocation }
128
+
129
+ export type { AwaitPorts, ClaimResult }
130
+ export { run_wake_await }
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { randomUUID } from 'node:crypto'
3
- import path from 'node:path'
4
3
  import { fileURLToPath } from 'node:url'
5
- import type { AgentProfile } from '#scripts/agent/agent-role-profile'
4
+ import type { AgentProfile, AgentProvider } from '#scripts/agent/agent-role-profile'
6
5
  import { backlog_budget } from '#scripts/backlog/backlog-budget'
7
6
  import { cli_flags } from '#scripts/lib/cli-flags'
8
- import { poll } from '#scripts/lib/poll'
9
7
  import { telegram_notify } from '#scripts/notify/telegram-notify'
10
8
  import { run_carry, type CarryRead } from '#scripts/run/carry/run-carry'
11
- import { run_carry_ended } from '#scripts/run/carry/run-carry-ended'
9
+ import { run_event_setup } from '#scripts/run/event/run-event-setup'
12
10
  import { run_event_stream } from '#scripts/run/event/run-event-stream'
13
11
  import { run_event_stream_emit } from '#scripts/run/event/run-event-stream-emit'
14
12
  import { run_headless } from '#scripts/run/run-headless'
15
13
  import { run_stranded_detect, type WatchedRun } from '#scripts/run/run-stranded-detect'
16
- import { run_wake, type RunWake, type WakeStopReason, type WakeTidyResult } from './run-wake'
14
+ import { run_wake, type WakeStopReason, type WakeTidyResult } from './run-wake'
15
+ import { run_wake_await } from './run-wake-await'
17
16
  import { run_wake_describe, type WakeContext } from './run-wake-describe'
18
17
  import { run_wake_driver } from './run-wake-driver'
19
18
  import { run_wake_handoff } from './run-wake-handoff'
20
19
  import { run_wake_loop, type LoopPorts, type LoopStop } from './run-wake-loop'
21
- import { run_wake_restart, type RestartPorts } from './run-wake-restart'
22
20
  import { run_wake_session, type LaunchResult } from './run-wake-session'
21
+ import { run_wake_stop } from './run-wake-stop'
22
+ import { run_wake_supervise } from './run-wake-supervise'
23
23
 
24
24
  // `josh run:wake --start | --list | --stop | --loop` — the supervisor that continues a cut
25
25
  // `backlogrun` or `queue` without a person retyping the keyword. **Which invocations it may continue
@@ -86,6 +86,7 @@ const STOP_BODIES: Record<WakeStopReason, string | undefined> = {
86
86
  unreadable:
87
87
  'The carry record could not be read, so the supervisor stopped rather than guess at it.',
88
88
  }
89
+ const NO_PROFILE_NOTE = 'the scheduler profile could not be resolved'
89
90
  const SUPERSEDED_RESULT: WakeTidyResult = 'superseded'
90
91
  const SUPERSEDED_NOTE =
91
92
  'another supervisor now holds the record, so it was left alone; this loop is the superseded one'
@@ -134,26 +135,14 @@ function group_count(values: ParsedValues): number {
134
135
  return verbs.filter((flag) => flag === true).length
135
136
  }
136
137
 
138
+ function context_of(directory: string): WakeContext {
139
+ return run_wake_describe.context_of(directory, note_to_stderr)
140
+ }
141
+
137
142
  async function resolve_context(): Promise<WakeContext | undefined> {
138
143
  const directory = await run_carry.repository_directory()
139
144
 
140
- if (directory === undefined) return undefined
141
-
142
- const log_target = run_wake.wake_log_path(directory)
143
-
144
- // Every verb reaches this one function, so creating the log here is what makes the path each of
145
- // them prints a path that exists — including `--list` and the already-running branch of `--start`,
146
- // neither of which launches anything.
147
- run_wake_session.ensure_log(log_target, note_to_stderr)
148
-
149
- return {
150
- carry_target: run_carry.carry_path(directory),
151
- ended_target: run_carry_ended.ended_path(directory),
152
- wake_target: run_wake.wake_path(directory),
153
- log_target,
154
- event_target: run_event_stream.target_of(directory),
155
- worktree: path.dirname(directory),
156
- }
145
+ return directory === undefined ? undefined : context_of(directory)
157
146
  }
158
147
 
159
148
  // There is nothing to supervise unless a run is actually carrying a budget. Refusing here rather than
@@ -165,6 +154,12 @@ function refuse_without_carry(kind: CarryRead['kind']): number {
165
154
  return report(NONE_VERDICT, FAILURE_EXIT_CODE)
166
155
  }
167
156
 
157
+ // **A session that started is said on the stream.** The driver's `waiting on judgment` is the moment it
158
+ // asked; from here a session is at work on the answer, until the supervisor's next hand-off.
159
+ function say_session_running(context: WakeContext): void {
160
+ run_event_setup.emit_to(context.event_target, run_event_setup.STAGE.SESSION, new Date())
161
+ }
162
+
168
163
  function wake_session(
169
164
  context: WakeContext,
170
165
  invocation: string,
@@ -180,8 +175,11 @@ function wake_session(
180
175
  // under `claude -p` a turn-end is the process's end (`run-headless.ts`).
181
176
  const environment = run_headless.environment()
182
177
  const target = { cwd: context.worktree, env: environment, log_path: context.log_target }
178
+ const result = run_wake_session.launch({ argv: built.argv, ...target, profile }, note_to_stderr)
183
179
 
184
- return run_wake_session.launch({ argv: built.argv, ...target, profile }, note_to_stderr)
180
+ if (result.kind === 'launched') say_session_running(context)
181
+
182
+ return result
185
183
  }
186
184
 
187
185
  // **A record this supervisor owns itself is not a session at work.** A failed driver leaves the record
@@ -287,14 +285,18 @@ async function finish(stop: LoopStop, context: WakeContext, started_at: string):
287
285
  return report(stop.reason, exit_code_of(stop.reason))
288
286
  }
289
287
 
290
- function spawn_supervisor(context: WakeContext, interval: string | undefined): number {
291
- const profile = run_wake_session.scheduler_profile()
292
- if (profile === undefined) return report(FAILED_VERDICT, FAILURE_EXIT_CODE)
288
+ function launch_supervisor(
289
+ context: WakeContext,
290
+ interval: string | undefined,
291
+ profile: AgentProfile | undefined,
292
+ ): LaunchResult {
293
+ if (profile === undefined) return { kind: 'failed', note: NO_PROFILE_NOTE }
293
294
  const argv = run_wake_session.supervisor_argv(SCRIPT_PATH, interval)
295
+
294
296
  // **The supervisor's own output goes to the same file as the sessions it starts.** It is detached
295
297
  // with the same call, so otherwise its `console.error` — the launch note that says the agent CLI is
296
298
  // not on `PATH`, and the stop note behind every warning — would go nowhere at all.
297
- const result = run_wake_session.launch(
299
+ return run_wake_session.launch(
298
300
  {
299
301
  argv,
300
302
  cwd: context.worktree,
@@ -303,6 +305,20 @@ function spawn_supervisor(context: WakeContext, interval: string | undefined): n
303
305
  },
304
306
  note_to_stderr,
305
307
  )
308
+ }
309
+
310
+ // The supervisor `josh backlogrun` starts beside its launched parent, on the parent's provider, from its
311
+ // own process so it inherits the credential the parent's Bash tool does not pass on (kit#3804). No carry
312
+ // record stands yet, so `start`'s check is skipped; its `--loop` waits for the parent to claim one.
313
+ function start_for_launch(git_directory: string, provider: AgentProvider): LaunchResult {
314
+ const resolved = run_wake_session.chosen_profile(provider)
315
+ if (resolved.kind === 'rejected') return { kind: 'failed', note: resolved.note }
316
+
317
+ return launch_supervisor(context_of(git_directory), undefined, resolved.profile)
318
+ }
319
+
320
+ function spawn_supervisor(context: WakeContext, interval: string | undefined): number {
321
+ const result = launch_supervisor(context, interval, run_wake_session.scheduler_profile())
306
322
 
307
323
  if (result.kind === 'failed') {
308
324
  console.error(`Could not start the supervisor: ${result.note}`)
@@ -345,40 +361,32 @@ function tidy_up(target: string): void {
345
361
  if (run_wake.tidy_own_wake(target) === SUPERSEDED_RESULT) note_to_stderr(SUPERSEDED_NOTE)
346
362
  }
347
363
 
348
- function restart_ports(context: WakeContext, ports: LoopPorts, interval_ms: number): RestartPorts {
349
- return {
350
- pass: async () => await run_wake_loop.run_loop(context.wake_target, ports, interval_ms),
351
- is_resumable: () => run_carry.read_carry(context.carry_target).kind === 'carried',
352
- pause: async () => {
353
- await poll.sleep(interval_ms)
354
- },
355
- note: note_to_stderr,
356
- }
357
- }
358
-
359
364
  // A failed pass is restarted while the carry record is still resumable (`run-wake-restart.ts`), so
360
- // only a failure past the restart bound reaches `finish` and its warning.
365
+ // only a failure past the restart bound reaches `finish` and its warning; the Claude lanes are brokered
366
+ // for as long as it supervises (`run-wake-supervise.ts`).
361
367
  // **The record is claimed once and held across every restart**, so a `--stop` landing in the pause
362
368
  // between two passes still finds it, signals this process, and the next pass reads the removal as the
363
369
  // person's stop rather than claiming a fresh record and resuming.
364
370
  async function loop(context: WakeContext, interval_ms: number): Promise<number> {
365
- const read = run_carry.read_carry(context.carry_target)
366
-
367
- if (read.kind !== 'carried') return refuse_without_carry(read.kind)
368
-
369
371
  const profile = run_wake_session.scheduler_profile()
370
372
  if (profile === undefined) return report(FAILED_VERDICT, FAILURE_EXIT_CODE)
371
373
 
372
- const claimed = run_wake.claim(context.wake_target, read.carry.invocation, new Date(), profile)
374
+ const claimed = await run_wake_await.claim_run(context, profile, interval_ms)
373
375
 
374
- if (claimed === undefined) return report(RUNNING_VERDICT)
376
+ if (claimed.kind === 'running') return report(RUNNING_VERDICT)
377
+ if (claimed.kind === 'refused') return refuse_without_carry(claimed.carry_kind)
375
378
 
376
- const ports = ports_for(context, claimed.profile ?? profile)
377
- const stop = await run_wake_restart.supervise(restart_ports(context, ports, interval_ms))
379
+ const ports = ports_for(context, claimed.wake.profile ?? profile)
380
+ const stop = await run_wake_supervise.supervise({
381
+ context,
382
+ ports,
383
+ interval_ms,
384
+ note: note_to_stderr,
385
+ })
378
386
 
379
387
  tidy_up(context.wake_target)
380
388
 
381
- return await finish(stop, context, read.carry.started_at)
389
+ return await finish(stop, context, claimed.carry.started_at)
382
390
  }
383
391
 
384
392
  // **The verdict tracks whether the supervisor is actually running, not merely whether a record is
@@ -394,30 +402,10 @@ function list(context: WakeContext): number {
394
402
  return report(run_wake.is_supervisor_live(wake) ? SUPERVISING_VERDICT : STALE_VERDICT)
395
403
  }
396
404
 
397
- // **Removing the record is the stop; the signal only shortens the wait.** A pid already gone, or one
398
- // this process may not signal, is not an error — the loop ends at its next pass either way, because it
399
- // re-reads the record it no longer finds and `run_wake.update_wake` refuses to write one back.
400
- function signal_supervisor(wake: RunWake): void {
401
- if (!run_wake.is_supervisor_live(wake)) return
402
-
403
- try {
404
- process.kill(wake.pid, 'SIGTERM')
405
- } catch {
406
- console.error(
407
- `Process ${String(wake.pid)} could not be signalled; the record is removed regardless.`,
408
- )
409
- }
410
- }
411
-
412
405
  function stop_supervisor(context: WakeContext): number {
413
- const wake = run_wake.read_wake(context.wake_target)
414
-
415
- if (wake === undefined) return report(NONE_VERDICT)
416
-
417
- run_wake.remove_wake(context.wake_target)
418
- signal_supervisor(wake)
406
+ const stopped = run_wake_stop.stop(context.wake_target)
419
407
 
420
- return report(STOPPED_VERDICT)
408
+ return report(stopped === undefined ? NONE_VERDICT : STOPPED_VERDICT)
421
409
  }
422
410
 
423
411
  async function dispatch(
@@ -465,6 +453,7 @@ const run_wake_cli = {
465
453
  ports_for,
466
454
  record_failure,
467
455
  run,
456
+ start_for_launch,
468
457
  stop_body,
469
458
  warning_body,
470
459
  }
@@ -1,17 +1,22 @@
1
+ import path from 'node:path'
1
2
  import { agent_role_profile } from '#scripts/agent/agent-role-profile'
2
3
  import { gh_spawn } from '#scripts/gh/gh-spawn'
3
4
  import { issue_citation } from '#scripts/rules/issue-citation'
4
5
  import { run_carry, type CarryRead, type RunCarry } from '#scripts/run/carry/run-carry'
6
+ import { run_carry_ended } from '#scripts/run/carry/run-carry-ended'
5
7
  import { run_event_stream } from '#scripts/run/event/run-event-stream'
6
8
  import { run_liveness } from '#scripts/run/run-liveness'
7
9
  import { run_wake, type RunWake, type WakeDecision, type WakeDecisionInput } from './run-wake'
10
+ import { run_wake_session } from './run-wake-session'
8
11
 
9
12
  // The `--list` presentation of a wake record. What the command *does* stays in `run-wake-cli.ts`; how
10
13
  // a supervisor's record reads back to a person lives here, one cohesive block.
11
14
 
12
15
  const UNKNOWN_CUTS = 'an unreadable number of'
13
16
  const REPO_LOOKUP_TIMEOUT_MS = 10_000
14
- const STOP_COMMAND = 'pnpm josh run:wake --stop'
17
+ // The whole run, not the supervisor alone: `run:wake --stop` leaves the run's record behind, and a
18
+ // record with no owner is what the next `josh backlogrun` stands on without starting.
19
+ const STOP_COMMAND = 'pnpm josh backlogrun --stop'
15
20
  const UNREADABLE_TIME = 'an unreadable time'
16
21
 
17
22
  // The three paths every verb needs: the carry record it reads, its own record, and the work tree a
@@ -30,6 +35,8 @@ interface WakeContext {
30
35
  // side uses. `--list` relays the newest event from here: the report surface belongs to the run, and
31
36
  // `--list` is the degenerate last-event read of the same stream the attached session follows.
32
37
  event_target: string
38
+ // The common git directory every record is keyed on — what finds the launch `josh backlogrun` made.
39
+ git_directory: string
33
40
  worktree: string
34
41
  }
35
42
 
@@ -156,7 +163,32 @@ function describe_wake_cited(wake: RunWake, context: WakeContext): string {
156
163
  return cite_description(describe_wake(wake, context), repo)
157
164
  }
158
165
 
159
- const run_wake_describe = { STOP_COMMAND, cite_description, describe_wake, describe_wake_cited }
166
+ function context_of(directory: string, note: (line: string) => void): WakeContext {
167
+ const log_target = run_wake.wake_log_path(directory)
168
+
169
+ // Every verb reaches this one function, so creating the log here is what makes the path each of
170
+ // them prints a path that exists — including `--list` and the already-running branch of `--start`,
171
+ // neither of which launches anything.
172
+ run_wake_session.ensure_log(log_target, note)
173
+
174
+ return {
175
+ carry_target: run_carry.carry_path(directory),
176
+ ended_target: run_carry_ended.ended_path(directory),
177
+ wake_target: run_wake.wake_path(directory),
178
+ log_target,
179
+ event_target: run_event_stream.target_of(directory),
180
+ git_directory: directory,
181
+ worktree: path.dirname(directory),
182
+ }
183
+ }
184
+
185
+ const run_wake_describe = {
186
+ STOP_COMMAND,
187
+ cite_description,
188
+ context_of,
189
+ describe_wake,
190
+ describe_wake_cited,
191
+ }
160
192
 
161
193
  export type { WakeContext }
162
194
  export { run_wake_describe }
@@ -184,6 +184,15 @@ async function run_driver(
184
184
  return await run_driver(target, args, wait, remaining - 1)
185
185
  }
186
186
 
187
+ // **A result handed to a session is said on the stream first.** The driver has stopped asking, so
188
+ // without it the board keeps the `handing off` this pass wrote and a run waiting on a session's
189
+ // judgment reads as one still getting ready.
190
+ async function announced(result: DriveResult): Promise<DriveResult> {
191
+ if (result.kind === 'judgment') await run_event_setup.emit(run_event_setup.STAGE.JUDGING)
192
+
193
+ return result
194
+ }
195
+
187
196
  async function drive(target: string, wait: Pause = pause): Promise<DriveResult> {
188
197
  const read = run_carry.read_carry(target)
189
198
  if (read.kind !== 'carried') return { kind: 'failed', note: 'The carry record changed.' }
@@ -197,10 +206,8 @@ async function drive(target: string, wait: Pause = pause): Promise<DriveResult>
197
206
 
198
207
  await run_event_setup.emit(run_event_setup.STAGE.HANDING_OFF)
199
208
 
200
- return read_result(
201
- await run_driver(target, args, wait, RERUN_LIMIT),
202
- target,
203
- read.carry.invocation,
209
+ return await announced(
210
+ read_result(await run_driver(target, args, wait, RERUN_LIMIT), target, read.carry.invocation),
204
211
  )
205
212
  }
206
213
 
@@ -1,6 +1,12 @@
1
1
  import { agent_argv, type AgentArgvResult } from '#scripts/agent/agent-argv'
2
- import { agent_role_profile, type AgentProfile } from '#scripts/agent/agent-role-profile'
2
+ import {
3
+ agent_role_profile,
4
+ type AgentProfile,
5
+ type AgentProvider,
6
+ type ProfileResult,
7
+ } from '#scripts/agent/agent-role-profile'
3
8
  import { claude_agent_argv } from '#scripts/agent/claude-agent-argv'
9
+ import { backlogrun_launch } from '#scripts/backlog/backlogrun-launch'
4
10
  import { detached_launch, type LaunchArgv } from '#scripts/run/detached-launch'
5
11
  import { run_invocation } from '#scripts/run/run-invocation'
6
12
 
@@ -46,10 +52,20 @@ function scheduler_profile(): AgentProfile | undefined {
46
52
  return undefined
47
53
  }
48
54
 
49
- function supervisor_environment(profile: AgentProfile): Record<string, string> {
55
+ // The scheduler profile of the provider a run was started on, whatever session the command was typed in
56
+ // (joshuafolkken/kit#3804).
57
+ function chosen_profile(provider: AgentProvider): ProfileResult {
58
+ const environment = backlogrun_launch.chosen_environment(provider)
59
+
60
+ return agent_role_profile.resolve(agent_role_profile.SCHEDULER, environment)
61
+ }
62
+
63
+ // **A Claude supervisor clears an inherited Codex session key**, so one started from a command typed in
64
+ // a Codex session does not read itself as both.
65
+ function supervisor_environment(profile: AgentProfile): Record<string, string | undefined> {
50
66
  return profile.provider === 'openai'
51
67
  ? { CODEX_THREAD_ID: SUPERVISOR_SESSION }
52
- : { CLAUDE_CODE_CHILD_SESSION: SUPERVISOR_SESSION }
68
+ : { CLAUDE_CODE_CHILD_SESSION: SUPERVISOR_SESSION, CODEX_THREAD_ID: undefined }
53
69
  }
54
70
 
55
71
  // **What comes back is always the rebuilt text, and it is refused unless it matches the record.** The
@@ -149,6 +165,7 @@ const run_wake_session = {
149
165
  ensure_log: detached_launch.ensure_log,
150
166
  is_safe_argv: detached_launch.is_safe_argv,
151
167
  launch: detached_launch.launch,
168
+ chosen_profile,
152
169
  scheduler_profile,
153
170
  supervisor_argv,
154
171
  supervisor_environment,
@@ -0,0 +1,37 @@
1
+ import { run_wake, type RunWake } from './run-wake'
2
+
3
+ // Stopping the supervisor, apart from the command that prints the verdict: `run:wake --stop` and
4
+ // `josh backlogrun --stop` both stop it here, so the one stop is never written twice.
5
+
6
+ // **Removing the record is the stop; the signal only shortens the wait.** A pid already gone, or one
7
+ // this process may not signal, is not an error — the loop ends at its next pass either way, because it
8
+ // re-reads the record it no longer finds and `run_wake.update_wake` refuses to write one back.
9
+ function signal(pid: number): void {
10
+ try {
11
+ process.kill(pid, 'SIGTERM')
12
+ } catch {
13
+ console.error(
14
+ `Process ${String(pid)} could not be signalled; the record is removed regardless.`,
15
+ )
16
+ }
17
+ }
18
+
19
+ function signal_supervisor(wake: RunWake): void {
20
+ if (run_wake.is_supervisor_live(wake)) signal(wake.pid)
21
+ }
22
+
23
+ // The record that was stopped, `undefined` where there was none to stop.
24
+ function stop(wake_target: string): RunWake | undefined {
25
+ const wake = run_wake.read_wake(wake_target)
26
+
27
+ if (wake === undefined) return undefined
28
+
29
+ run_wake.remove_wake(wake_target)
30
+ signal_supervisor(wake)
31
+
32
+ return wake
33
+ }
34
+
35
+ const run_wake_stop = { signal, stop }
36
+
37
+ export { run_wake_stop }
@@ -0,0 +1,44 @@
1
+ import { claude_lane_broker } from '#scripts/lane/claude-lane-broker'
2
+ import { poll } from '#scripts/lib/poll'
3
+ import { run_carry } from '#scripts/run/carry/run-carry'
4
+ import type { WakeContext } from './run-wake-describe'
5
+ import { run_wake_loop, type LoopPorts, type LoopStop } from './run-wake-loop'
6
+ import { run_wake_restart, type RestartPorts } from './run-wake-restart'
7
+
8
+ // The supervisor's whole life: the restarting loop (`run-wake-restart.ts`), with the Claude lanes
9
+ // brokered beside it.
10
+ //
11
+ // **The lanes are brokered for the supervisor's life, not one drive's**: a lane's ship asks for its
12
+ // reviewer or its relaunch whenever it gets there, a hand-back to a session included, and only this
13
+ // process holds the token those sessions sign in with (joshuafolkken/kit#3817, `claude-lane-broker.ts`).
14
+
15
+ interface SuperviseTarget {
16
+ context: WakeContext
17
+ ports: LoopPorts
18
+ interval_ms: number
19
+ note: (text: string) => void
20
+ }
21
+
22
+ function restart_ports(target: SuperviseTarget): RestartPorts {
23
+ const { context, ports, interval_ms } = target
24
+
25
+ return {
26
+ pass: async () => await run_wake_loop.run_loop(context.wake_target, ports, interval_ms),
27
+ is_resumable: () => run_carry.read_carry(context.carry_target).kind === 'carried',
28
+ pause: async () => {
29
+ await poll.sleep(interval_ms)
30
+ },
31
+ note: target.note,
32
+ }
33
+ }
34
+
35
+ async function supervise(target: SuperviseTarget): Promise<LoopStop> {
36
+ return await claude_lane_broker.serve_during(
37
+ async () => await run_wake_restart.supervise(restart_ports(target)),
38
+ )
39
+ }
40
+
41
+ const run_wake_supervise = { supervise }
42
+
43
+ export { run_wake_supervise }
44
+ export type { SuperviseTarget }