@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
@@ -0,0 +1,240 @@
1
+ import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
2
+ import { git_gh_conditional } from '#scripts/gh/git-gh-conditional'
3
+ import { git_gh_issue_read } from '#scripts/gh/git-gh-issue-read'
4
+ import { git_command } from '#scripts/git/git-command'
5
+ import { PROJECT_ROOT } from '#scripts/init/init-paths'
6
+ import { stamp_file } from '#scripts/josh/stamp-file'
7
+ import { json_value } from '#scripts/lib/json-value'
8
+ import { z } from 'zod'
9
+ import { issue_watch_diff, type WatchSnapshot } from './issue-watch-diff'
10
+
11
+ // The record of what a run last read of its issue, and the poll that compares the issue against it.
12
+ //
13
+ // **The baseline is written where the run reads the issue** — `josh run:entry`, which prints the body
14
+ // and every comment — so it is the same for a dispatched lane child and a `fullrun` in a person's own
15
+ // checkout, and a resume that prints nothing keeps the baseline it had. One record per checkout,
16
+ // keyed on its root like the review attestation, carries the issue number, so the hook needs no other
17
+ // way to learn which issue the session works on.
18
+ //
19
+ // **A poll is a conditional request.** The issue object's ETag changes with its body and with its
20
+ // comment count, so a 304 there means neither moved and the comments are not read at all.
21
+
22
+ const RECORD_PREFIX = 'josh-issue-watch-'
23
+ const CLOSED_STATE = 'closed'
24
+
25
+ const record_schema = z.object({
26
+ issue: z.string(),
27
+ body: z.string(),
28
+ last_comment_id: z.number(),
29
+ acknowledged_at: z.string(),
30
+ checked_at: z.string(),
31
+ etag: z.string().optional(),
32
+ pending: z.string().optional(),
33
+ shown: z.string().optional(),
34
+ })
35
+
36
+ const issue_schema = z.looseObject({ body: z.string().nullish(), state: z.string() })
37
+
38
+ type WatchRecord = z.infer<typeof record_schema>
39
+
40
+ type Fetched =
41
+ | { kind: 'unchanged' }
42
+ | { kind: 'unreadable' }
43
+ | { kind: 'closed' }
44
+ | { kind: 'read'; snapshot: WatchSnapshot; etag: string | undefined }
45
+
46
+ const UNREADABLE: Fetched = { kind: 'unreadable' }
47
+
48
+ function record_path(root: string = PROJECT_ROOT): string {
49
+ return stamp_file.stamp_path(RECORD_PREFIX, root)
50
+ }
51
+
52
+ function read_record(root?: string): WatchRecord | undefined {
53
+ const raw = stamp_file.read_stamp_text(record_path(root))
54
+
55
+ return raw === undefined ? undefined : json_value.parse_with(raw, record_schema)
56
+ }
57
+
58
+ function write_record(record: WatchRecord, root?: string): WatchRecord {
59
+ stamp_file.replace_stamp(record_path(root), record)
60
+
61
+ return record
62
+ }
63
+
64
+ function clear(root?: string): void {
65
+ stamp_file.remove_stamp(record_path(root))
66
+ }
67
+
68
+ interface FetchRequest {
69
+ issue: string
70
+ etag?: string | undefined
71
+ timeout_ms?: number | undefined
72
+ }
73
+
74
+ async function read_snapshot(
75
+ request: FetchRequest,
76
+ body: string,
77
+ etag: string | undefined,
78
+ ): Promise<Fetched> {
79
+ const comments = issue_watch_diff.parse_comments(
80
+ await git_gh_issue_read.issue_list_comments(request.issue, request.timeout_ms),
81
+ )
82
+
83
+ return comments === undefined ? UNREADABLE : { kind: 'read', snapshot: { body, comments }, etag }
84
+ }
85
+
86
+ async function fetch_issue(request: FetchRequest): Promise<Fetched> {
87
+ const read = await git_gh_conditional.read_conditional(
88
+ git_gh_api_path.issue_api_path(request.issue),
89
+ request.etag,
90
+ request.timeout_ms,
91
+ )
92
+
93
+ if (read.kind !== 'changed') return read
94
+
95
+ const fields = json_value.parse_with(read.body, issue_schema)
96
+
97
+ if (fields === undefined) return UNREADABLE
98
+ if (fields.state === CLOSED_STATE) return { kind: 'closed' }
99
+
100
+ return await read_snapshot(request, fields.body ?? '', read.etag)
101
+ }
102
+
103
+ // `acknowledged_at` is when a person's update was last taken in — the baseline's own read, or an
104
+ // acknowledgement that had something to take in. The review attestation compares it with the brief.
105
+ function fresh_record(
106
+ issue: string,
107
+ fetched: Extract<Fetched, { kind: 'read' }>,
108
+ now: Date,
109
+ acknowledged_at: string = now.toISOString(),
110
+ ): WatchRecord {
111
+ const baseline = issue_watch_diff.baseline_of(fetched.snapshot)
112
+ const checked_at = now.toISOString()
113
+
114
+ return { issue, ...baseline, acknowledged_at, checked_at, etag: fetched.etag }
115
+ }
116
+
117
+ // Called by `josh run:entry` once it has printed the issue. `false` when the issue could not be read,
118
+ // which leaves no record — a watch with no baseline would call the whole issue unread.
119
+ async function record_baseline(
120
+ issue: string,
121
+ root?: string,
122
+ now: Date = new Date(),
123
+ ): Promise<boolean> {
124
+ const fetched = await fetch_issue({ issue })
125
+
126
+ if (fetched.kind !== 'read') return false
127
+
128
+ write_record(fresh_record(issue, fetched, now), root)
129
+
130
+ return true
131
+ }
132
+
133
+ // The `_here` forms key the record on the repository root rather than `process.cwd()`, the same reason
134
+ // `review_attest.check_here` does: a run started from a subdirectory must find the record it wrote.
135
+ async function record_baseline_here(issue: string): Promise<boolean> {
136
+ return await record_baseline(issue, await git_command.repository_root())
137
+ }
138
+
139
+ async function clear_here(): Promise<void> {
140
+ clear(await git_command.repository_root())
141
+ }
142
+
143
+ function with_fetched(record: WatchRecord, fetched: Fetched, checked_at: string): WatchRecord {
144
+ if (fetched.kind !== 'read') return { ...record, checked_at }
145
+
146
+ const pending = issue_watch_diff.unread_diff(record, fetched.snapshot)
147
+
148
+ return { ...record, checked_at, etag: fetched.etag, pending }
149
+ }
150
+
151
+ interface PollOptions {
152
+ interval_ms: number
153
+ timeout_ms?: number
154
+ now?: Date
155
+ }
156
+
157
+ function is_due(record: WatchRecord, options: PollOptions, now: Date): boolean {
158
+ return now.getTime() - Date.parse(record.checked_at) >= options.interval_ms
159
+ }
160
+
161
+ async function refresh(
162
+ record: WatchRecord,
163
+ options: PollOptions,
164
+ now: Date,
165
+ root?: string,
166
+ ): Promise<WatchRecord | undefined> {
167
+ const fetched = await fetch_issue({
168
+ issue: record.issue,
169
+ etag: record.etag,
170
+ timeout_ms: options.timeout_ms,
171
+ })
172
+
173
+ if (fetched.kind === 'closed') {
174
+ clear(root)
175
+
176
+ return undefined
177
+ }
178
+
179
+ return write_record(with_fetched(record, fetched, now.toISOString()), root)
180
+ }
181
+
182
+ // The checkout's record, polled when the last poll is at least `interval_ms` old. `undefined` when no
183
+ // run recorded a baseline here, or the issue has closed — a closed issue ends its watch.
184
+ async function poll(options: PollOptions, root?: string): Promise<WatchRecord | undefined> {
185
+ const record = read_record(root)
186
+ const now = options.now ?? new Date()
187
+
188
+ if (record === undefined || !is_due(record, options, now)) return record
189
+
190
+ return await refresh(record, options, now, root)
191
+ }
192
+
193
+ // Marks a pending update shown, so the hook stops once per update rather than on every call.
194
+ function mark_shown(record: WatchRecord, root?: string): WatchRecord {
195
+ return write_record({ ...record, shown: record.pending }, root)
196
+ }
197
+
198
+ interface Acknowledged {
199
+ issue: string
200
+ diff: string | undefined
201
+ }
202
+
203
+ // `josh issue:ack`: read the issue as it is now, return what changed since the baseline, and make
204
+ // that the new baseline. **What is returned is exactly what is acknowledged** — the read and the new
205
+ // baseline are the same snapshot, so an update landing between the two cannot be acknowledged unseen.
206
+ async function acknowledge(
207
+ root?: string,
208
+ now: Date = new Date(),
209
+ ): Promise<Acknowledged | undefined> {
210
+ const record = read_record(root)
211
+
212
+ if (record === undefined) return undefined
213
+
214
+ const fetched = await fetch_issue({ issue: record.issue })
215
+
216
+ if (fetched.kind !== 'read') return undefined
217
+
218
+ const diff = issue_watch_diff.unread_diff(record, fetched.snapshot)
219
+
220
+ const acknowledged_at = diff === undefined ? record.acknowledged_at : now.toISOString()
221
+
222
+ write_record(fresh_record(record.issue, fetched, now, acknowledged_at), root)
223
+
224
+ return { issue: record.issue, diff }
225
+ }
226
+
227
+ const issue_watch = {
228
+ acknowledge,
229
+ clear,
230
+ clear_here,
231
+ mark_shown,
232
+ poll,
233
+ read_record,
234
+ record_baseline,
235
+ record_baseline_here,
236
+ record_path,
237
+ }
238
+
239
+ export type { Acknowledged, PollOptions, WatchRecord }
240
+ export { issue_watch }
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { execSync } from 'node:child_process'
3
3
  import { fileURLToPath } from 'node:url'
4
+ import { gh_author_association } from '#scripts/gh/gh-author-association'
4
5
  import { git_gh_issue_read } from '#scripts/gh/git-gh-issue-read'
5
6
  import { parse_json_object_or_undefined } from '#scripts/git/parse-json-array'
6
7
  import { issue_number_shape } from '#scripts/issue/issue-number-shape'
@@ -24,8 +25,6 @@ import { session_cite } from './session-cite'
24
25
  const FAILURE_EXIT_CODE = 1
25
26
  const ARGV_OFFSET = 2
26
27
  const ISSUE_FIELDS = 'body,author_association'
27
- const TRUSTED_ASSOCIATIONS: ReadonlySet<string> = new Set(['OWNER', 'MEMBER', 'COLLABORATOR'])
28
- const TRUSTED_LIST = [...TRUSTED_ASSOCIATIONS].join(' / ')
29
28
  const UNKNOWN_ASSOCIATION = 'unknown'
30
29
  const USAGE = 'Usage: josh measure:rerun <issue-number>'
31
30
  const NO_BASELINE = 'no `command → value` baseline found in ## ベースライン — nothing to re-run'
@@ -100,10 +99,10 @@ async function read_trusted_body(issue_number: string): Promise<BodyRead> {
100
99
 
101
100
  const association = issue.author_association ?? UNKNOWN_ASSOCIATION
102
101
 
103
- if (!TRUSTED_ASSOCIATIONS.has(association)) {
102
+ if (!gh_author_association.is_trusted(association)) {
104
103
  return {
105
104
  kind: 'refused',
106
- reason: `refusing to run the baseline of ${session_cite.issue(issue_number)}: its author is ${association}, not ${TRUSTED_LIST}`,
105
+ reason: `refusing to run the baseline of ${session_cite.issue(issue_number)}: its author is ${association}, not ${gh_author_association.TRUSTED_LIST}`,
107
106
  }
108
107
  }
109
108
 
@@ -54,6 +54,7 @@ const ALIASES: Record<string, string> = {
54
54
  pf: 'profile',
55
55
  st: 'start',
56
56
  sy: 'sync',
57
+ un: 'uninstall',
57
58
  rmi: 'registry:migrate',
58
59
  g: 'git',
59
60
  gp: 'pr',
@@ -79,9 +80,12 @@ const ALIASES: Record<string, string> = {
79
80
  rvf: 'review:findings',
80
81
  ruv: 'rule:value',
81
82
  ev: 'eval',
83
+ fr: 'fresh:run',
84
+ as: 'auth:setup',
82
85
  lsm: 'lane:sample',
83
86
  lst: 'lane:stats',
84
87
  lli: 'lane:limit',
88
+ lrs: 'lane:restart',
85
89
  blr: 'backlogrun',
86
90
  }
87
91
 
@@ -61,6 +61,17 @@ const AI_COMMANDS: Record<string, CommandEntry> = {
61
61
  category: 'AI tools',
62
62
  reference: ['[--exclude <n>[,<n>...]] [--label <name>]', 'automation', ['network']],
63
63
  },
64
+ 'auto-ok:judge': {
65
+ script: 'scripts/auto-ok/auto-ok-judge-cli.ts',
66
+ description:
67
+ 'List the open issues backlogrun owes an auto-ok judgement, or record one (--apply / --decline)',
68
+ category: 'AI tools',
69
+ reference: [
70
+ '[--apply <n> (--lane | --solo) | --decline <n> --reason <text>]',
71
+ 'automation',
72
+ ['network'],
73
+ ],
74
+ },
64
75
  ...BACKLOG_COMMANDS,
65
76
  cost: {
66
77
  script: 'scripts/cost-runtime/cost-cli.ts',
@@ -11,10 +11,10 @@ const BACKLOG_COMMANDS: Record<string, CommandEntry> = {
11
11
  backlogrun: {
12
12
  script: 'scripts/backlog/backlogrun-cli.ts',
13
13
  description:
14
- 'Start a backlogrun in the background and show its board; with one already running, only show the board',
14
+ 'Start a backlogrun in the background and show its board; with one already running, only show the board, or replace it when it is idle and the invocation differs; --stop stops it',
15
15
  category: 'AI tools',
16
16
  reference: [
17
- '[#<n>...] [--only] [--max <n>] [--idle <minutes>] [--agent claude|codex]',
17
+ '[<n>...] [--only] [--max <n>] [--idle <minutes>] [--agent claude|codex] | --stop',
18
18
  'developer',
19
19
  ['files', 'network', 'processes'],
20
20
  ],
@@ -181,7 +181,7 @@ const DEV_COMMANDS: Record<string, CommandEntry> = {
181
181
  'test:unit': {
182
182
  script: 'scripts/test/test-unit-guard.ts',
183
183
  description:
184
- 'Run unit tests with Vitest (skips when Vitest is absent; fails when it has no tests)',
184
+ 'Run unit tests with Vitest (without Vitest: a basic project runs its own test script, others skip; fails when Vitest has no tests)',
185
185
  category: 'Development',
186
186
  reference: [FILTER_ARGUMENTS, 'developer', ['processes']],
187
187
  core_weight: gate_plan.direct_unit_weight,
@@ -11,6 +11,13 @@ const ISSUES_WITH_REPO = '<issue...> [--repo <owner/repo>]'
11
11
 
12
12
  /* eslint-disable @typescript-eslint/naming-convention */
13
13
  const ISSUE_COMMANDS: Record<string, CommandEntry> = {
14
+ 'issue:ack': {
15
+ script: 'scripts/issue/issue-ack-cli.ts',
16
+ description:
17
+ "Record that the run read a person's update to its issue, printing the update it acknowledges",
18
+ category: 'AI tools',
19
+ reference: ['<issue>', 'automation', ['network']],
20
+ },
14
21
  'issue:read': {
15
22
  script: 'scripts/issue/issue-read-cli.ts',
16
23
  description: "Print each issue's title, body and every comment on it, in one call",
@@ -68,7 +75,7 @@ const ISSUE_COMMANDS: Record<string, CommandEntry> = {
68
75
  'File an Issue with every filing step: lint, Origin, fold, WIP cap, duplicate scout, labels, then epic:bundle',
69
76
  category: 'AI tools',
70
77
  reference: [
71
- '<title> --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>] [--distinct <N,…>] [--over-cap] [--no-auto-ok] [--requested] [--release]',
78
+ '<title> --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>] [--distinct <N,…>] [--over-cap] [--no-auto-ok] [--release]',
72
79
  'automation',
73
80
  ['network'],
74
81
  ],
@@ -76,9 +83,9 @@ const ISSUE_COMMANDS: Record<string, CommandEntry> = {
76
83
  'issue:release': {
77
84
  script: 'scripts/issue/issue-release-cli.ts',
78
85
  description:
79
- "Link an Issue to the repository's open release Issue as its blocker, filing one when none is open",
86
+ "Link an Issue to the repository's open release Issue as its blocker (or, with --wait, as waiting on it), filing one when none is open",
80
87
  category: 'AI tools',
81
- reference: ['<issue>', 'automation', ['network']],
88
+ reference: ['<issue> [--wait]', 'automation', ['network']],
82
89
  },
83
90
  }
84
91
  /* eslint-enable @typescript-eslint/naming-convention */
@@ -40,7 +40,7 @@ const LANE_COMMANDS: Record<string, CommandEntry> = {
40
40
  'lane:prune': {
41
41
  ...LANE_ARGUMENTS,
42
42
  description:
43
- 'Close every lane an interruption left without its work tree, then sweep unregistered leftovers under the lanes root',
43
+ 'Close every lane an interruption left without its work tree and every spent lane, then sweep unregistered leftovers under the lanes root',
44
44
  category: 'AI tools',
45
45
  reference: ['', 'automation', ['files', 'git']],
46
46
  default_script_arguments: ['prune'],
@@ -74,6 +74,15 @@ const LANE_COMMANDS: Record<string, CommandEntry> = {
74
74
  category: 'AI tools',
75
75
  reference: ['<issue> [--stash <message>]', 'automation', ['files', 'git', 'processes']],
76
76
  },
77
+ // `.env` is read for `JOSH_LANE_ROOT`, as every lane command reads it, so the lane is found.
78
+ 'lane:restart': {
79
+ script: 'scripts/lane/lane-restart-cli.ts',
80
+ tsx_arguments: OPTIONAL_ENV_FILE_FLAGS,
81
+ description:
82
+ 'Stop a running lane child, stash its uncommitted work and re-dispatch its issue in a fresh session — not counted as an outage or a failure',
83
+ category: 'AI tools',
84
+ reference: ['<issue>', 'developer', ['files', 'git', 'processes', 'network']],
85
+ },
77
86
  // A live run's lane limit: `.env` is read so the bare form reports the
78
87
  // `JOSH_LANE_LIMIT` a person set there when no override stands.
79
88
  'lane:limit': {
@@ -97,7 +106,7 @@ const LANE_COMMANDS: Record<string, CommandEntry> = {
97
106
  'lane:stats': {
98
107
  ...MEASURE_ARGUMENTS,
99
108
  description:
100
- 'Print one table row of lane throughput, gate duration and machine load over a period, and the per-stage durations of a lane under it',
109
+ 'Print one table row of lane throughput, gate duration and machine load over a period, the per-stage durations of a lane under it, and the wait after each ship stop reason under those',
101
110
  category: 'AI tools',
102
111
  reference: ['--period <days> [--limit <lane-limit>]', 'maintainer', ['none']],
103
112
  default_script_arguments: ['stats'],
@@ -17,7 +17,7 @@ const PROJECT_COMMANDS: Record<string, CommandEntry> = {
17
17
  description: 'Set a project up for the GitHub Issue workflow, from git init to the setup PR',
18
18
  category: 'Project',
19
19
  reference: [
20
- `${PROFILE_ARGUMENTS} [--yes] [--github] [--public] [--init-command <command>] [--push-preflight-command <command>]`,
20
+ `${PROFILE_ARGUMENTS} [--yes] [--github] [--public] [--dry-run] [--init-command <command>] [--push-preflight-command <command>]`,
21
21
  'developer',
22
22
  ['files', 'git', 'network', 'processes'],
23
23
  ],
@@ -29,12 +29,30 @@ const PROJECT_COMMANDS: Record<string, CommandEntry> = {
29
29
  reference: ['<dir>', 'automation', ['files', 'git']],
30
30
  is_kit_only: true,
31
31
  },
32
+ 'fresh:run': {
33
+ script: 'scripts/fresh-machine/fresh-machine-cli.ts',
34
+ description:
35
+ 'Run an install scenario in a Docker container of a machine with nothing installed, and keep the log',
36
+ category: 'Project',
37
+ reference: [
38
+ '<pattern> | --spec <file.json> [--package-version <version>] [--log-dir <dir>] [--rebuild] | --list',
39
+ 'maintainer',
40
+ ['files', 'network', 'processes'],
41
+ ],
42
+ },
43
+ 'auth:setup': {
44
+ script: 'scripts/auth/auth-setup-cli.ts',
45
+ description:
46
+ 'Save the long-lived Claude Code token `claude setup-token` prints in the macOS keychain, for josh fresh:run',
47
+ category: 'Project',
48
+ reference: ['[--check]', 'maintainer', ['network', 'processes']],
49
+ },
32
50
  init: {
33
51
  script: 'scripts/init/init.ts',
34
52
  description: 'Initialize config in a new project',
35
53
  category: 'Project',
36
54
  reference: [
37
- `${PROFILE_ARGUMENTS} [--no-install]`,
55
+ `${PROFILE_ARGUMENTS} [--no-install] [--dry-run]`,
38
56
  'developer',
39
57
  ['files', 'network', 'processes'],
40
58
  ],
@@ -45,6 +63,12 @@ const PROJECT_COMMANDS: Record<string, CommandEntry> = {
45
63
  category: 'Project',
46
64
  reference: ['', 'developer', ['files']],
47
65
  },
66
+ uninstall: {
67
+ script: 'scripts/uninstall/uninstall.ts',
68
+ description: 'Remove what kit added to a project and list what is left to review by hand',
69
+ category: 'Project',
70
+ reference: [`${PROFILE_ARGUMENTS} [--yes]`, 'developer', ['files', 'processes']],
71
+ },
48
72
  'registry:migrate': {
49
73
  script: 'scripts/registry-migration/migrate.ts',
50
74
  description: 'Migrate a project from GitHub Packages to public npm',
@@ -35,7 +35,7 @@ import { status_icons } from '#scripts/lib/status-icons'
35
35
  // gate's plan line is what says "the gate starts here", and it is single-sourced for the reason the
36
36
  // verdict is: the printer builds it and the detector matches it from one place.
37
37
 
38
- const { FAIL_ICON, PASS_ICON } = status_icons
38
+ const { FAIL_ICON, PASS_ICON, WARN_ICON } = status_icons
39
39
  const GATE_SUBJECT = 'verification gate'
40
40
 
41
41
  // The prefixes are what the detector matches **and** what the printer builds its line from, so a
@@ -43,16 +43,41 @@ const GATE_SUBJECT = 'verification gate'
43
43
  // this module exists to prevent.
44
44
  const GATE_PASSED_PREFIX = `${PASS_ICON} ${GATE_SUBJECT} passed`
45
45
  const GATE_FAILED_PREFIX = `${FAIL_ICON} ${GATE_SUBJECT} failed`
46
+ // **A gate that ran no check has not passed, and it has not failed either.** Every check of a `basic`
47
+ // project with nothing to check exits 0 with a skip notice, which is a legitimate state — the exit code
48
+ // stays 0, or such a project could never commit — but a line reading `passed` made it indistinguishable
49
+ // from a gate that ran every check. It opens with neither icon and neither word, so no reader of the
50
+ // other two prefixes can take it for one of them.
51
+ const GATE_UNVERIFIED_PREFIX = `${WARN_ICON} ${GATE_SUBJECT} not verified`
46
52
 
47
- type JoshVerdict = 'passed' | 'failed'
53
+ type JoshVerdict = 'passed' | 'failed' | 'unverified'
48
54
 
49
55
  // Typed rather than left to inference: the namespace object widens a bare literal to `string`, and a
50
56
  // caller comparing `read_verdict(line)` against it would then be comparing two unrelated types.
51
57
  const PASSED_VERDICT: JoshVerdict = 'passed'
52
58
  const FAILED_VERDICT: JoshVerdict = 'failed'
59
+ const UNVERIFIED_VERDICT: JoshVerdict = 'unverified'
53
60
 
54
- function format_gate_passed(step_count: string, total: string): string {
55
- return `${GATE_PASSED_PREFIX} (${step_count} checks) in ${total}.`
61
+ // How many of a gate's checks ran, and how many passed without running.
62
+ interface GateCounts {
63
+ ran: number
64
+ skipped: number
65
+ }
66
+
67
+ // The split is printed only when there is one. A gate that skipped nothing — kit's own, and an ordinary
68
+ // consumer's — keeps the line it always printed.
69
+ function format_counts(counts: GateCounts): string {
70
+ if (counts.skipped === 0) return `${String(counts.ran)} checks`
71
+
72
+ return `${String(counts.ran)} ran, ${String(counts.skipped)} skipped`
73
+ }
74
+
75
+ function format_gate_passed(counts: GateCounts, total: string): string {
76
+ return `${GATE_PASSED_PREFIX} (${format_counts(counts)}) in ${total}.`
77
+ }
78
+
79
+ function format_gate_unverified(skipped: number, total: string): string {
80
+ return `${GATE_UNVERIFIED_PREFIX} (0 ran, ${String(skipped)} skipped) in ${total}.`
56
81
  }
57
82
 
58
83
  function format_gate_failed(failed_labels: string, total: string): string {
@@ -91,14 +116,18 @@ function read_verdict(line: string): JoshVerdict | undefined {
91
116
 
92
117
  if (trimmed.startsWith(GATE_PASSED_PREFIX)) return PASSED_VERDICT
93
118
 
119
+ if (trimmed.startsWith(GATE_UNVERIFIED_PREFIX)) return UNVERIFIED_VERDICT
120
+
94
121
  return undefined
95
122
  }
96
123
 
97
124
  const josh_verdict = {
98
125
  PASSED_VERDICT,
99
126
  FAILED_VERDICT,
127
+ UNVERIFIED_VERDICT,
100
128
  format_gate_passed,
101
129
  format_gate_failed,
130
+ format_gate_unverified,
102
131
  GATE_OPENING_MARK,
103
132
  GATE_OPENING_PREFIX,
104
133
  is_gate_opening,
@@ -52,6 +52,7 @@ const PROBE_ENVIRONMENT = { TZ: 'UTC', LC_ALL: 'C' }
52
52
  // `process.kill(pid, 0)` runs every permission check and delivers nothing, so it is the standard
53
53
  // liveness probe: it throws `ESRCH` where the process is gone.
54
54
  const LIVENESS_SIGNAL = 0
55
+ const NO_SUCH_PROCESS = 'ESRCH'
55
56
  // Below this a pid is not a process: `0` addresses the caller's own process group and anything
56
57
  // negative addresses another group, so a truncated or hand-edited record carrying either would
57
58
  // otherwise answer "alive" forever.
@@ -79,18 +80,28 @@ interface StartProbes {
79
80
  read_ps_start: (pid: number) => string | undefined
80
81
  }
81
82
 
82
- function is_live_pid(pid: number): boolean {
83
- if (pid < LOWEST_REAL_PID) return false
84
-
83
+ // The code the liveness probe was refused with, or nothing where the caller may signal the process.
84
+ function signal_refusal(pid: number): string | undefined {
85
85
  try {
86
86
  process.kill(pid, LIVENESS_SIGNAL)
87
87
 
88
- return true
89
- } catch {
90
- return false
88
+ return undefined
89
+ } catch (error) {
90
+ return (error as NodeJS.ErrnoException).code ?? NO_SUCH_PROCESS
91
91
  }
92
92
  }
93
93
 
94
+ function is_live_pid(pid: number): boolean {
95
+ return pid >= LOWEST_REAL_PID && signal_refusal(pid) === undefined
96
+ }
97
+
98
+ // **Whether a pid names any process at all, for a reader that may not be allowed to signal it.** Only
99
+ // `ESRCH` proves the process gone: `EPERM` is the kernel refusing a signal to a process it has just
100
+ // found — one under another user, or outside a sandbox that may only signal its own — so it is present.
101
+ function is_present_pid(pid: number): boolean {
102
+ return pid >= LOWEST_REAL_PID && signal_refusal(pid) !== NO_SUCH_PROCESS
103
+ }
104
+
94
105
  // Linux exposes field 22 as the process start time in clock ticks since boot. The command name in
95
106
  // field 2 may itself contain spaces or `)`, so parsing starts after its final closing parenthesis;
96
107
  // `state` is then index 0 and `starttime` index 19. A direct file read avoids process enumeration and
@@ -315,6 +326,19 @@ function is_same_process(
315
326
  return matches_recorded_start(pid, recorded_start, read)
316
327
  }
317
328
 
329
+ // `is_same_process` for a reader that may not be allowed to signal the writer (`is_present_pid`): a
330
+ // refused signal still compares the start time, so a reissued pid separates from its writer here too.
331
+ function is_present_process(
332
+ pid: number,
333
+ recorded_start: string | undefined,
334
+ read: (pid: number) => string | undefined = read_start,
335
+ ): boolean | undefined {
336
+ if (!is_present_pid(pid)) return false
337
+ if (recorded_start === undefined) return undefined
338
+
339
+ return matches_recorded_start(pid, recorded_start, read)
340
+ }
341
+
318
342
  // **Whether a record is the caller's own, which is a different question from whether its writer is
319
343
  // alive**. `is_same_process` asks about the recorded process; this asks
320
344
  // whether that process *is this one*. A supervisor that has been replaced reads its successor's
@@ -339,6 +363,8 @@ const process_identity = {
339
363
  is_live_beacon,
340
364
  is_live_pid,
341
365
  is_own_process,
366
+ is_present_pid,
367
+ is_present_process,
342
368
  is_same_process,
343
369
  open_beacon,
344
370
  own_fields,