@ohos-ports/nx 23.3.0-beta.2 → 24.0.2-beta.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 (294) hide show
  1. package/README.md +3 -6
  2. package/dist/bin/nx.js +2 -2
  3. package/dist/bin/post-install.js +5 -3
  4. package/dist/nx-cloud-internals.d.ts +1 -0
  5. package/dist/nx-cloud-internals.js +7 -1
  6. package/dist/src/adapter/ngcli-adapter.js +2 -2
  7. package/dist/src/ai/clone-ai-config-repo.js +49 -12
  8. package/dist/src/command-line/add/add.d.ts +2 -0
  9. package/dist/src/command-line/add/add.js +23 -5
  10. package/dist/src/command-line/affected/affected.js +2 -2
  11. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +0 -12
  12. package/dist/src/command-line/graph/graph.js +38 -10
  13. package/dist/src/command-line/init/command-object.js +0 -12
  14. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +21 -62
  15. package/dist/src/command-line/init/init-v2.js +0 -2
  16. package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +1 -1
  17. package/dist/src/command-line/migrate/agentic/close-agent-session.js +4 -3
  18. package/dist/src/command-line/migrate/agentic/definitions.d.ts +1 -3
  19. package/dist/src/command-line/migrate/agentic/definitions.js +5 -97
  20. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +7 -45
  21. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +9 -51
  22. package/dist/src/command-line/migrate/agentic/handoff.d.ts +1 -43
  23. package/dist/src/command-line/migrate/agentic/handoff.js +3 -119
  24. package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +0 -21
  25. package/dist/src/command-line/migrate/agentic/instruction-files.js +6 -27
  26. package/dist/src/command-line/migrate/agentic/master/run-master-session.d.ts +19 -1
  27. package/dist/src/command-line/migrate/agentic/master/run-master-session.js +88 -49
  28. package/dist/src/command-line/migrate/agentic/master/spawn-master.d.ts +1 -0
  29. package/dist/src/command-line/migrate/agentic/master/spawn-master.js +86 -24
  30. package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +2 -2
  31. package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +2 -2
  32. package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +1 -12
  33. package/dist/src/command-line/migrate/agentic/prompts/fragments.js +8 -17
  34. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +0 -15
  35. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +3 -100
  36. package/dist/src/command-line/migrate/agentic/select.d.ts +12 -6
  37. package/dist/src/command-line/migrate/agentic/select.js +38 -8
  38. package/dist/src/command-line/migrate/agentic/types.d.ts +9 -68
  39. package/dist/src/command-line/migrate/agentic/types.js +6 -8
  40. package/dist/src/command-line/migrate/command-object.js +6 -10
  41. package/dist/src/command-line/migrate/execute-migration.d.ts +6 -6
  42. package/dist/src/command-line/migrate/execute-migration.js +25 -15
  43. package/dist/src/command-line/migrate/migrate-analytics.d.ts +18 -1
  44. package/dist/src/command-line/migrate/migrate-analytics.js +38 -0
  45. package/dist/src/command-line/migrate/migrate-commits.d.ts +29 -17
  46. package/dist/src/command-line/migrate/migrate-commits.js +86 -32
  47. package/dist/src/command-line/migrate/migrate-config.d.ts +6 -6
  48. package/dist/src/command-line/migrate/migrate-config.js +12 -6
  49. package/dist/src/command-line/migrate/migrate-output.d.ts +1 -6
  50. package/dist/src/command-line/migrate/migrate-output.js +1 -10
  51. package/dist/src/command-line/migrate/migrate.d.ts +6 -30
  52. package/dist/src/command-line/migrate/migrate.js +257 -255
  53. package/dist/src/command-line/migrate/resolve-package-version.d.ts +2 -4
  54. package/dist/src/command-line/migrate/resolve-package-version.js +2 -35
  55. package/dist/src/command-line/migrate/run/agent-output.d.ts +2 -0
  56. package/dist/src/command-line/migrate/run/agent-output.js +9 -0
  57. package/dist/src/command-line/migrate/run/broker.d.ts +14 -5
  58. package/dist/src/command-line/migrate/run/broker.js +69 -21
  59. package/dist/src/command-line/migrate/run/clean-retry.js +4 -2
  60. package/dist/src/command-line/migrate/run/existing-run-report.d.ts +1 -1
  61. package/dist/src/command-line/migrate/run/existing-run-report.js +7 -5
  62. package/dist/src/command-line/migrate/run/give-up.d.ts +4 -3
  63. package/dist/src/command-line/migrate/run/give-up.js +8 -5
  64. package/dist/src/command-line/migrate/run/index.d.ts +7 -5
  65. package/dist/src/command-line/migrate/run/index.js +16 -1
  66. package/dist/src/command-line/migrate/run/issues.js +7 -2
  67. package/dist/src/command-line/migrate/run/orchestrator.d.ts +61 -4
  68. package/dist/src/command-line/migrate/run/orchestrator.js +397 -133
  69. package/dist/src/command-line/migrate/run/run-state.d.ts +17 -1
  70. package/dist/src/command-line/migrate/run/run-state.js +41 -2
  71. package/dist/src/command-line/migrate/run/runbook.d.ts +1 -0
  72. package/dist/src/command-line/migrate/run/runbook.js +115 -18
  73. package/dist/src/command-line/migrate/run/state-lock.d.ts +29 -4
  74. package/dist/src/command-line/migrate/run/state-lock.js +102 -10
  75. package/dist/src/command-line/migrate/run/state-machine.js +4 -1
  76. package/dist/src/command-line/migrate/run/util.d.ts +7 -2
  77. package/dist/src/command-line/migrate/run/util.js +30 -3
  78. package/dist/src/command-line/migrate/run/worker.d.ts +1 -1
  79. package/dist/src/command-line/migrate/run/worker.js +252 -250
  80. package/dist/src/command-line/migrate/sort-migrations.d.ts +1 -7
  81. package/dist/src/command-line/migrate/sort-migrations.js +1 -12
  82. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +17 -12
  83. package/dist/src/command-line/release/changelog.js +9 -6
  84. package/dist/src/command-line/release/config/config.d.ts +1 -0
  85. package/dist/src/command-line/release/config/config.js +48 -9
  86. package/dist/src/command-line/release/release.js +2 -2
  87. package/dist/src/command-line/release/utils/git.js +8 -7
  88. package/dist/src/command-line/release/utils/interpolate-release-config.d.ts +6 -0
  89. package/dist/src/command-line/release/utils/interpolate-release-config.js +39 -0
  90. package/dist/src/command-line/release/utils/release-graph.d.ts +5 -0
  91. package/dist/src/command-line/release/utils/release-graph.js +1 -0
  92. package/dist/src/command-line/release/utils/remote-release-clients/github.js +5 -6
  93. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +5 -6
  94. package/dist/src/command-line/release/utils/resolve-changelog-renderer.js +2 -2
  95. package/dist/src/command-line/release/utils/shared.js +10 -10
  96. package/dist/src/command-line/release/utils/version-plan-utils.d.ts +3 -12
  97. package/dist/src/command-line/release/utils/version-plan-utils.js +33 -55
  98. package/dist/src/command-line/release/version/version-actions.js +2 -1
  99. package/dist/src/command-line/release/version.js +5 -2
  100. package/dist/src/command-line/reset/reset.js +58 -62
  101. package/dist/src/command-line/run/executor-utils.js +1 -0
  102. package/dist/src/command-line/run/run-one.d.ts +20 -1
  103. package/dist/src/command-line/run/run-one.js +153 -5
  104. package/dist/src/command-line/run/run.js +2 -1
  105. package/dist/src/command-line/show/projects.js +5 -3
  106. package/dist/src/command-line/show/show-target/info.d.ts +10 -1
  107. package/dist/src/command-line/show/show-target/info.js +100 -15
  108. package/dist/src/command-line/show/show-target/inputs.js +10 -2
  109. package/dist/src/command-line/show/show-target/utils.js +1 -0
  110. package/dist/src/command-line/watch/command-object.js +3 -22
  111. package/dist/src/command-line/watch/watch.d.ts +0 -7
  112. package/dist/src/command-line/watch/watch.js +1 -1
  113. package/dist/src/command-line/yargs-utils/shared-options.js +1 -1
  114. package/dist/src/config/misc-interfaces.d.ts +18 -0
  115. package/dist/src/config/nx-json.d.ts +22 -11
  116. package/dist/src/config/task-graph.d.ts +1 -1
  117. package/dist/src/config/workspace-json-project-json.d.ts +88 -0
  118. package/dist/src/core/graph/main.js +1 -1
  119. package/dist/src/core/graph/runtime.js +1 -1
  120. package/dist/src/core/graph/styles.css +1 -1
  121. package/dist/src/core/graph/styles.js +1 -1
  122. package/dist/src/daemon/client/client.d.ts +15 -0
  123. package/dist/src/daemon/client/client.js +181 -71
  124. package/dist/src/daemon/client/daemon-socket-messenger.d.ts +1 -1
  125. package/dist/src/daemon/client/daemon-socket-messenger.js +2 -2
  126. package/dist/src/daemon/server/client-socket-context.js +1 -1
  127. package/dist/src/daemon/server/handle-process-in-background.js +21 -9
  128. package/dist/src/daemon/server/handle-select-affected-tasks.d.ts +2 -1
  129. package/dist/src/daemon/server/handle-select-affected-tasks.js +21 -13
  130. package/dist/src/daemon/server/native-js-loader.d.ts +1 -0
  131. package/dist/src/daemon/server/native-js-loader.js +21 -0
  132. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +4 -8
  133. package/dist/src/daemon/server/server.d.ts +2 -1
  134. package/dist/src/daemon/server/server.js +17 -4
  135. package/dist/src/daemon/server/shutdown-utils.js +0 -3
  136. package/dist/src/daemon/server/start.d.ts +1 -0
  137. package/dist/src/daemon/server/start.js +2 -0
  138. package/dist/src/daemon/socket-utils.d.ts +11 -9
  139. package/dist/src/daemon/socket-utils.js +31 -61
  140. package/dist/src/daemon/tmp-dir.d.ts +19 -14
  141. package/dist/src/daemon/tmp-dir.js +142 -354
  142. package/dist/src/devkit-exports.d.ts +9 -1
  143. package/dist/src/devkit-exports.js +12 -1
  144. package/dist/src/devkit-internals.d.ts +6 -5
  145. package/dist/src/devkit-internals.js +13 -4
  146. package/dist/src/executors/run-commands/run-commands.impl.d.ts +4 -2
  147. package/dist/src/executors/run-commands/run-commands.impl.js +53 -27
  148. package/dist/src/executors/run-commands/running-tasks.d.ts +13 -4
  149. package/dist/src/executors/run-commands/running-tasks.js +148 -95
  150. package/dist/src/executors/run-commands/schema.json +1 -1
  151. package/dist/src/hasher/check-task-files.js +5 -8
  152. package/dist/src/hasher/file-hasher.js +9 -7
  153. package/dist/src/hasher/hash-task.js +4 -0
  154. package/dist/src/hasher/native-task-hasher-impl.js +3 -1
  155. package/dist/src/hasher/task-hasher.d.ts +2 -0
  156. package/dist/src/hasher/task-hasher.js +4 -1
  157. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +1 -2
  158. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +6 -11
  159. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +2 -2
  160. package/dist/src/migrations/update-24-0-0/enable-legacy-affected.d.ts +4 -0
  161. package/dist/src/migrations/update-24-0-0/enable-legacy-affected.js +15 -0
  162. package/dist/src/migrations/update-24-0-0/enable-legacy-affected.md +30 -0
  163. package/dist/src/migrations/update-24-0-0/find-custom-hashers.d.ts +3 -0
  164. package/dist/src/migrations/update-24-0-0/find-custom-hashers.js +100 -0
  165. package/dist/src/migrations/update-24-0-0/find-custom-hashers.md +63 -0
  166. package/dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md +146 -0
  167. package/dist/src/migrations/update-24-0-0/rename-ultracache-ignore-options.d.ts +3 -0
  168. package/dist/src/migrations/update-24-0-0/rename-ultracache-ignore-options.js +130 -0
  169. package/dist/src/migrations/update-24-0-0/rename-ultracache-ignore-options.md +62 -0
  170. package/dist/src/native/assert-supported-platform.js +1 -1
  171. package/dist/src/native/index.d.ts +238 -18
  172. package/dist/src/native/index.js +6 -1
  173. package/dist/src/native/native-bindings.js +15 -6
  174. package/dist/src/native/nx.openharmony-arm64.node +0 -0
  175. package/dist/src/native/nx.wasi-browser.js +1 -0
  176. package/dist/src/native/nx.wasi.cjs +1 -0
  177. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  178. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  179. package/dist/src/nx-cloud/update-manager.d.ts +14 -0
  180. package/dist/src/nx-cloud/update-manager.js +237 -50
  181. package/dist/src/plugins/js/lock-file/pnpm-parser.js +3 -4
  182. package/dist/src/plugins/js/lock-file/pruned-output.d.ts +2 -2
  183. package/dist/src/plugins/js/lock-file/pruned-output.js +4 -4
  184. package/dist/src/plugins/js/utils/register.js +9 -4
  185. package/dist/src/project-graph/affected/affected-tasks.d.ts +9 -6
  186. package/dist/src/project-graph/affected/affected-tasks.js +31 -7
  187. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +7 -0
  188. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +49 -6
  189. package/dist/src/project-graph/utils/project-configuration/target-merging.js +1 -1
  190. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +12 -133
  191. package/dist/src/project-graph/utils/project-configuration-utils.js +2 -2
  192. package/dist/src/project-graph/utils/retrieve-workspace-files.js +27 -6
  193. package/dist/src/tasks-runner/create-task-graph.d.ts +8 -7
  194. package/dist/src/tasks-runner/create-task-graph.js +50 -27
  195. package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
  196. package/dist/src/tasks-runner/init-tasks-runner.js +116 -59
  197. package/dist/src/tasks-runner/life-cycle.d.ts +4 -1
  198. package/dist/src/tasks-runner/life-cycle.js +7 -0
  199. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +5 -2
  200. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +13 -7
  201. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.d.ts +4 -3
  202. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +9 -2
  203. package/dist/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.js +5 -2
  204. package/dist/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.js +4 -1
  205. package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +5 -1
  206. package/dist/src/tasks-runner/pseudo-ipc.d.ts +1 -0
  207. package/dist/src/tasks-runner/pseudo-ipc.js +4 -0
  208. package/dist/src/tasks-runner/pseudo-terminal.d.ts +1 -1
  209. package/dist/src/tasks-runner/pseudo-terminal.js +2 -2
  210. package/dist/src/tasks-runner/readiness/executor-ready-signal.d.ts +7 -0
  211. package/dist/src/tasks-runner/readiness/executor-ready-signal.js +43 -0
  212. package/dist/src/tasks-runner/readiness/probes.d.ts +14 -0
  213. package/dist/src/tasks-runner/readiness/probes.js +101 -0
  214. package/dist/src/tasks-runner/readiness/ready-signal-server.d.ts +18 -0
  215. package/dist/src/tasks-runner/readiness/ready-signal-server.js +72 -0
  216. package/dist/src/tasks-runner/readiness/ready-signal.d.ts +33 -0
  217. package/dist/src/tasks-runner/readiness/ready-signal.js +92 -0
  218. package/dist/src/tasks-runner/readiness/ready-when.d.ts +84 -0
  219. package/dist/src/tasks-runner/readiness/ready-when.js +371 -0
  220. package/dist/src/tasks-runner/readiness/startup-host.d.ts +1 -0
  221. package/dist/src/tasks-runner/readiness/startup-host.js +32 -0
  222. package/dist/src/tasks-runner/run-command.d.ts +6 -2
  223. package/dist/src/tasks-runner/run-command.js +21 -31
  224. package/dist/src/tasks-runner/run-outcome.d.ts +16 -0
  225. package/dist/src/tasks-runner/run-outcome.js +39 -0
  226. package/dist/src/tasks-runner/running-tasks/batch-process.js +6 -0
  227. package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -4
  228. package/dist/src/tasks-runner/running-tasks/running-task.d.ts +1 -1
  229. package/dist/src/tasks-runner/task-env.d.ts +10 -0
  230. package/dist/src/tasks-runner/task-env.js +37 -5
  231. package/dist/src/tasks-runner/task-graph-utils.d.ts +9 -6
  232. package/dist/src/tasks-runner/task-graph-utils.js +17 -12
  233. package/dist/src/tasks-runner/task-orchestrator.d.ts +62 -18
  234. package/dist/src/tasks-runner/task-orchestrator.js +750 -150
  235. package/dist/src/tasks-runner/tasks-runner.d.ts +1 -1
  236. package/dist/src/tasks-runner/tasks-schedule.d.ts +18 -1
  237. package/dist/src/tasks-runner/tasks-schedule.js +63 -6
  238. package/dist/src/tasks-runner/tokens.d.ts +54 -0
  239. package/dist/src/tasks-runner/tokens.js +161 -0
  240. package/dist/src/tasks-runner/utils.d.ts +35 -6
  241. package/dist/src/tasks-runner/utils.js +149 -68
  242. package/dist/src/ultracache/ci-status.d.ts +24 -0
  243. package/dist/src/ultracache/ci-status.js +87 -0
  244. package/dist/src/ultracache/config.d.ts +3 -3
  245. package/dist/src/ultracache/config.js +4 -4
  246. package/dist/src/ultracache/fetch.d.ts +2 -0
  247. package/dist/src/ultracache/fetch.js +1 -0
  248. package/dist/src/ultracache/overrides.js +4 -0
  249. package/dist/src/utils/acknowledge-build-scripts.d.ts +34 -1
  250. package/dist/src/utils/acknowledge-build-scripts.js +171 -32
  251. package/dist/src/utils/child-process.js +2 -1
  252. package/dist/src/utils/find-matching-projects.js +9 -6
  253. package/dist/src/utils/git-utils.d.ts +12 -5
  254. package/dist/src/utils/git-utils.js +69 -11
  255. package/dist/src/utils/long-running-target.d.ts +1 -3
  256. package/dist/src/utils/long-running-target.js +1 -3
  257. package/dist/src/utils/output.d.ts +4 -3
  258. package/dist/src/utils/output.js +4 -3
  259. package/dist/src/utils/owned-private-dir.d.ts +1 -17
  260. package/dist/src/utils/owned-private-dir.js +10 -62
  261. package/dist/src/utils/package-manager.d.ts +8 -0
  262. package/dist/src/utils/package-manager.js +65 -8
  263. package/dist/src/utils/perf-hooks.d.ts +1 -0
  264. package/dist/src/utils/plugins/core-plugins.js +5 -1
  265. package/dist/src/utils/progress-topics.d.ts +1 -0
  266. package/dist/src/utils/progress-topics.js +1 -0
  267. package/dist/src/utils/safe-spawn.js +1 -5
  268. package/dist/src/utils/shell-quoting.d.ts +8 -1
  269. package/dist/src/utils/shell-quoting.js +12 -1
  270. package/dist/src/utils/spawn-without-terminal.d.ts +27 -0
  271. package/dist/src/utils/spawn-without-terminal.js +110 -0
  272. package/dist/src/utils/string-similarity.d.ts +38 -0
  273. package/dist/src/utils/string-similarity.js +108 -0
  274. package/migrations.json +20 -6
  275. package/package.json +15 -24
  276. package/schemas/migrations-schema.json +11 -0
  277. package/schemas/nx-schema.json +133 -11
  278. package/schemas/project-schema.json +114 -4
  279. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +0 -56
  280. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +0 -67
  281. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +0 -49
  282. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +0 -53
  283. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +0 -26
  284. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +0 -27
  285. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +0 -70
  286. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +0 -113
  287. package/dist/src/command-line/migrate/agentic/run-step.d.ts +0 -68
  288. package/dist/src/command-line/migrate/agentic/run-step.js +0 -148
  289. package/dist/src/command-line/migrate/agentic/runner.d.ts +0 -21
  290. package/dist/src/command-line/migrate/agentic/runner.js +0 -262
  291. package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.d.ts +0 -9
  292. package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.js +0 -18
  293. package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +0 -18
  294. package/dist/src/tasks-runner/legacy-depends-on-warning.js +0 -109
@@ -9,6 +9,19 @@ export declare const RUN_STATE_FILE_NAME = "run.json";
9
9
  * with fails closed as corrupt instead of being dispensed.
10
10
  */
11
11
  export declare const SHELL_SAFE_VALUE: RegExp;
12
+ export type MigrationIdProblem = 'not-shell-safe' | 'duplicate';
13
+ /**
14
+ * The ids of `migrations` that no orchestrated run can take, each once, in the
15
+ * order found. A worker finds its step by migration id, so an id the plan
16
+ * lists twice can't name one step.
17
+ */
18
+ export declare function migrationIdProblems(migrations: ReadonlyArray<{
19
+ package: string;
20
+ name: string;
21
+ }>): {
22
+ id: string;
23
+ problem: MigrationIdProblem;
24
+ }[];
12
25
  export declare const ISSUE_ID: RegExp;
13
26
  declare const MIGRATE_RUN_STATUSES: readonly ['active', 'completed'];
14
27
  export type MigrateRunStatus = (typeof MIGRATE_RUN_STATUSES)[number];
@@ -128,8 +141,10 @@ export interface MigrateRunState {
128
141
  noProgress?: MigrateRunNoProgress;
129
142
  checkpointFailed?: boolean;
130
143
  treeOperation?: MigrateTreeOperation;
144
+ singleMigration?: boolean;
131
145
  analytics: MigrateRunAnalytics;
132
146
  }
147
+ export declare function runKind(state: MigrateRunState): 'single' | 'active' | 'finished';
133
148
  export declare const TREE_OPERATION_KINDS: readonly ['commit', 'install', 'fold-install', 'action-install', 'reset', 'give-up', 'checkpoint'];
134
149
  export type MigrateTreeOperationKind = (typeof TREE_OPERATION_KINDS)[number];
135
150
  export interface MigrateTreeOperation {
@@ -187,7 +202,8 @@ export interface UninterpretableRunDir {
187
202
  *
188
203
  * A dir that reads cleanly as a finished run is skipped whatever its name is:
189
204
  * it competes with nothing, and reporting it would block every future run
190
- * with no way for retention to ever clear it.
205
+ * with no way for retention to ever clear it. A `--run-migration` run's record
206
+ * is skipped too: nothing continues it, and a leftover must not block a run.
191
207
  *
192
208
  * Throws {@link NewerRunStateFormatError} when any run dir holds a
193
209
  * newer-format run.json: whether that run is active can't be determined
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NewerRunStateFormatError = exports.TREE_OPERATION_KINDS = exports.MIGRATE_ISSUE_DISPOSITIONS = exports.TERMINAL_STEP_STATUSES = exports.ISSUE_ID = exports.SHELL_SAFE_VALUE = exports.RUN_STATE_FILE_NAME = exports.CURRENT_RUN_STATE_FORMAT_VERSION = void 0;
4
+ exports.migrationIdProblems = migrationIdProblems;
4
5
  exports.issueFingerprint = issueFingerprint;
6
+ exports.runKind = runKind;
5
7
  exports.migrateRunsDir = migrateRunsDir;
6
8
  exports.runDir = runDir;
7
9
  exports.runHandoffsDir = runHandoffsDir;
@@ -32,6 +34,26 @@ exports.RUN_STATE_FILE_NAME = 'run.json';
32
34
  * with fails closed as corrupt instead of being dispensed.
33
35
  */
34
36
  exports.SHELL_SAFE_VALUE = /^[A-Za-z0-9@/:._-]+$/;
37
+ /**
38
+ * The ids of `migrations` that no orchestrated run can take, each once, in the
39
+ * order found. A worker finds its step by migration id, so an id the plan
40
+ * lists twice can't name one step.
41
+ */
42
+ function migrationIdProblems(migrations) {
43
+ const seen = new Set();
44
+ const problems = new Map();
45
+ for (const m of migrations) {
46
+ const id = `${m.package}:${m.name}`;
47
+ if (!exports.SHELL_SAFE_VALUE.test(id)) {
48
+ problems.set(id, 'not-shell-safe');
49
+ }
50
+ else if (seen.has(id) && !problems.has(id)) {
51
+ problems.set(id, 'duplicate');
52
+ }
53
+ seen.add(id);
54
+ }
55
+ return [...problems].map(([id, problem]) => ({ id, problem }));
56
+ }
35
57
  // `new Date().toISOString()`, the only shape Nx writes. Retention and active-run
36
58
  // selection compare these lexicographically, and the value is rendered into the
37
59
  // stdout the agent scans for blocks, so neither a different notation nor an
@@ -117,6 +139,21 @@ exports.MIGRATE_ISSUE_DISPOSITIONS = [
117
139
  'resolved',
118
140
  'deferred-final',
119
141
  ];
142
+ // A `--run-migration` record is 'single' whatever its status.
143
+ function runKind(state) {
144
+ if (state.singleMigration === true)
145
+ return 'single';
146
+ switch (state.status) {
147
+ case 'active':
148
+ return 'active';
149
+ case 'completed':
150
+ return 'finished';
151
+ default: {
152
+ const unhandled = state.status;
153
+ throw new Error(`Unhandled migrate run status: ${unhandled}`);
154
+ }
155
+ }
156
+ }
120
157
  exports.TREE_OPERATION_KINDS = [
121
158
  'commit',
122
159
  'install',
@@ -409,6 +446,7 @@ function hasValidRunStateShape(parsed) {
409
446
  isOptionalBoolean(parsed.checkpointFailed) &&
410
447
  isOptionalBoolean(parsed.skipInstall) &&
411
448
  isOptionalBoolean(parsed.validate) &&
449
+ isOptionalBoolean(parsed.singleMigration) &&
412
450
  isOptionalSha(parsed.gitRefAtInit) &&
413
451
  isOptionalMatching(RUNBOOK_NAME, parsed.runbookPath) &&
414
452
  isOptionalString(parsed.branch) &&
@@ -566,7 +604,8 @@ function readRunDirState(candidateDir) {
566
604
  *
567
605
  * A dir that reads cleanly as a finished run is skipped whatever its name is:
568
606
  * it competes with nothing, and reporting it would block every future run
569
- * with no way for retention to ever clear it.
607
+ * with no way for retention to ever clear it. A `--run-migration` run's record
608
+ * is skipped too: nothing continues it, and a leftover must not block a run.
570
609
  *
571
610
  * Throws {@link NewerRunStateFormatError} when any run dir holds a
572
611
  * newer-format run.json: whether that run is active can't be determined
@@ -598,7 +637,7 @@ function findActiveRun(root) {
598
637
  });
599
638
  continue;
600
639
  }
601
- if (state.status !== 'active')
640
+ if (runKind(state) !== 'active')
602
641
  continue;
603
642
  // Run ids are joined into paths and interpolated into dispensed commands,
604
643
  // so a dir whose name fails the gate is never trusted as a resumable run.
@@ -8,6 +8,7 @@ export interface RunbookContext {
8
8
  createCommits: boolean;
9
9
  validate: boolean;
10
10
  finalValidation: boolean;
11
+ singleMigration: boolean;
11
12
  }
12
13
  /**
13
14
  * Every interpolated value is collapsed to a single line: the rendered content
@@ -22,8 +22,9 @@ exports.RUNBOOK_FILE_NAME = 'RUNBOOK.md';
22
22
  function renderRunbook(ctx) {
23
23
  const runId = (0, text_1.singleLine)(ctx.runId);
24
24
  const reconcile = (0, text_1.singleLine)(ctx.reconcileCommand);
25
- // Run-scoped like the Claude runner grant: a wildcard run segment would let
26
- // a configured child write handoffs that settle steps in another run.
25
+ // Run-scoped like the Claude Code handoff grant: a wildcard run segment
26
+ // would let a configured child write handoffs that settle steps in another
27
+ // run.
27
28
  const handoffsGlob = `${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${runId}/${types_1.HANDOFFS_DIR_NAME}/**`;
28
29
  const lines = [
29
30
  `# Nx migrate run ${runId}`,
@@ -57,9 +58,17 @@ function renderRunbook(ctx) {
57
58
  `2. Do what the block says. For a \`next-step\` action, run the given`,
58
59
  ` worker command; it applies one migration.`,
59
60
  `3. When a step hands work back to you (see "Agent work" below), run the`,
60
- ` \`next\` command first: its response restates the work, names the`,
61
- ` handoff file to write, and lists any recorded issues assigned to the`,
62
- ` step. Do the work, write the handoff file, then run \`next\` again.`,
61
+ ...(ctx.singleMigration
62
+ ? [
63
+ ` \`next\` command first: its response restates the work and names`,
64
+ ` the handoff file to write. Do the work, write the handoff file,`,
65
+ ` then run \`next\` again.`,
66
+ ]
67
+ : [
68
+ ` \`next\` command first: its response restates the work, names the`,
69
+ ` handoff file to write, and lists any recorded issues assigned to the`,
70
+ ` step. Do the work, write the handoff file, then run \`next\` again.`,
71
+ ]),
63
72
  `4. Repeat until the orchestrator reports the run \`complete\`.`,
64
73
  ``,
65
74
  `Execute only what the current block asks. Do not run migrations the`,
@@ -72,9 +81,39 @@ function renderRunbook(ctx) {
72
81
  `repeated instructions or report the blocker to the user; re-running the`,
73
82
  `reconcile command alone changes nothing.`,
74
83
  ``,
75
- `An \`existing-run\` action means no run was started because one is`,
76
- `already active. Show its report to the user and let them choose between`,
77
- `the two commands it lists; do not choose for them.`,
84
+ ...(ctx.singleMigration
85
+ ? []
86
+ : [
87
+ `An \`existing-run\` action means no run was started because one is`,
88
+ `already active. Show its report to the user and let them choose between`,
89
+ `the two commands it lists; do not choose for them.`,
90
+ ``,
91
+ ]),
92
+ `## Long-running commands`,
93
+ ``,
94
+ `A worker or \`next\` command can wait for minutes on a dependency install`,
95
+ `or a commit. When Nx started this session, the nx process that started it`,
96
+ `runs them, and the waiting command prints an \`[nx]\` line after 15`,
97
+ `seconds and then once a minute: what nx is running and for how long.`,
98
+ ``,
99
+ `- If your shell tool can run a command in the background, start worker and`,
100
+ ` \`next\` commands that way and check their output about once a minute.`,
101
+ ` Each time, tell the user in one sentence what the latest \`[nx]\` line`,
102
+ ` says.`,
103
+ `- Claude Code: start each worker and \`next\` command with`,
104
+ ` \`run_in_background\`, then start a Monitor on its output that passes`,
105
+ ` \`[nx]\` lines through. A \`next\` command can wait as long as a worker.`,
106
+ `- Stopping a worker or \`next\` command does not stop the install or the`,
107
+ ` commit it waits on. If one seems stuck, tell the user they can quit this`,
108
+ ...(ctx.singleMigration
109
+ ? [
110
+ ` session, then press Ctrl+C in the terminal to end it, and run the`,
111
+ ` same \`nx migrate --run-migration\` command again afterwards.`,
112
+ ]
113
+ : [
114
+ ` session, then press Ctrl+C in the terminal to end it, and resume the run`,
115
+ ` afterwards.`,
116
+ ]),
78
117
  ``,
79
118
  `## Agent work`,
80
119
  ``,
@@ -83,9 +122,7 @@ function renderRunbook(ctx) {
83
122
  `- A migration with an AI-driven part makes its worker emit a`,
84
123
  ` \`nx_migrate_prompt\` block pointing at an instructions file. Apply`,
85
124
  ` those instructions to the workspace. Scope rules for that work:`,
86
- ...(0, fragments_1.renderAuthorScopeRuleLines)((0, text_1.singleLine)(ctx.pmExec), {
87
- source: 'dispensed-step',
88
- }).map((line) => ` ${line}`),
125
+ ...(0, fragments_1.renderAuthorScopeRuleLines)((0, text_1.singleLine)(ctx.pmExec)).map((line) => ` ${line}`),
89
126
  NX_JSON_MIGRATE_RULE,
90
127
  ` The \`nx migrate\` commands this runbook and the step blocks hand you`,
91
128
  ` (the reconcile, worker and \`next\` commands) are the exception to the`,
@@ -97,16 +134,76 @@ function renderRunbook(ctx) {
97
134
  if (ctx.finalValidation) {
98
135
  lines.push(`- The run's last step is a validation pass over the whole workspace,`, ` dispensed once every migration step is done. Its \`nx_migrate_prompt\``, ` block points at an instructions file; that file names the base ref its`, ` \`nx affected\` runs use. Scope rules for that work:`, ...(0, fragments_1.renderFinalValidationScopeRuleLines)().map((line) => ` ${line}`), NX_JSON_MIGRATE_RULE);
99
136
  }
100
- lines.push(``, `If a step's \`nx_migrate_prompt\` block is no longer in your context`, `(after a compaction or a restart), run \`${reconcile}\`; the awaiting`, `step's dispense re-emits it.`, ``, `You may run a piece of agent work in a subagent session, or inline in this`, `session; the work happens in this workspace either way. Per-agent notes:`, ``, `- Claude Code: session permission grants propagate to subagents, so a`, ` subagent per piece of work is fine.`, `- Codex: subagents inherit the live sandbox and approval settings.`, `- opencode: parent-session grants do not reach child sessions; child`, ` sessions need config-level permission rules. Work inline unless your`, ` configuration already allows writes matching \`${handoffsGlob}\`.`, ``, `### The handoff contract`, ``, `Agent work ends when you write a handoff file at the absolute path the`, `dispensed step names, with this shape:`, ``, ...(0, fragments_1.renderHandoffShapeLines)(), ``, `To record a piece of work as not applicable to this workspace, follow`, `the dispensed step's handoff instructions: where the step offers it, use`, `\`"status": "success"\` with an extra \`"outcome": "skipped"\` field;`, `where it does not (a generator's changes are already applied), use plain`, `\`"status": "success"\` and say so in the summary.`, ``, `How to end a piece of agent work:`, ``, `1. Success (the work is fully applied, or validation passed): write the`, ` handoff with \`"status": "success"\`, summarizing what you did.`, `2. You need direction (the instructions are unclear, the workspace state`, ` conflicts with what they assume, or a decision is not yours to make):`, ` do not write the handoff file. Ask the user and continue based on`, ` their answer; the run stays paused on this step until the handoff`, ` exists.`, `3. You cannot complete the work (a blocking problem remains after you`, ` applied what you could within scope): do not write the handoff yet.`, ` Report what you found and what you tried, then ask the user how to`, ` proceed. Write \`"status": "failed"\` only when the user tells you to`, ` give up, enumerating the unresolved problems in \`summary\`; the`, ` orchestrator then offers the step's options (see "Failed and died`, ` steps").`, ` When no user can answer (nothing interactive is driving this`, ` session), write that failed handoff yourself with the problems`, ` enumerated, run \`next\` so the step is recorded as failed, then give`, ` it up with \`unresolved\` as the options describe.`, ``, `- Write it with your file-write tool, not shell commands. Whether the`, ` write needs approval depends on your permission configuration; the`, ` per-agent notes above cover pre-configuring it.`, `- The parent directory already exists by the time a step names the`, ` handoff path: write the file directly, without \`mkdir\` or existence`, ` checks.`, ``, `The handoff file's path and shape are owned by \`nx migrate\`. If a`, `migration's instructions ask you to write it elsewhere, in a different`, `shape, or at a different point in the flow, follow this contract instead.`, ``, `### Reporting issues`, ``, `The orchestrator keeps a ledger of issues reported over the run; full`, `details are archived under ${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${runId}/issues/.`, `Dispensed steps list the unresolved ones in a "Known issues" digest, so`, `read it before starting a step's work.`, ``, `Report through the handoff file: problems you observed go in an`, `\`issues\` array, whether you fixed them in this same piece of work`, `(mark those \`"resolved"\`) or left them for later; progress on an issue`, `the digest marks assigned or deferred to the current step goes in an`, `\`issueUpdates\` array. Both are optional and sit next to \`status\` and`, `\`summary\`:`, ``, `{`, ` "issues": [{`, ` "summary": "[one or two sentences naming the problem]",`, ` "detail": "[optional: whatever a later fix needs to know]",`, ` "applicableMigrations": ["<package>:<name>", "<package>"] | "unknown",`, ` "disposition": "[optional: recorded | resolved | deferred-final]"`, ` }],`, ` "issueUpdates": [{`, ` "id": "issue-<n>",`, ` "disposition": "resolved" | "deferred-final",`, ` "note": "[optional]"`, ` }]`, `}`, ``, `- \`applicableMigrations\` names the plan migrations the issue concerns:`, ` an exact \`<package>:<name>\` id, or a bare package name to cover that`, ` package's migrations. Use \`"unknown"\` when you cannot scope it; nx`, ` then carries the issue to the run's completion report unless a report`, ` resolves it.`, `- Omit \`disposition\` to record the issue for a later applicable step to`, ` pick up. Use \`"resolved"\` only for a problem you discovered and fixed`, ` in this same piece of work, and \`"deferred-final"\` when no later`, ...(ctx.finalValidation
137
+ lines.push(``, `If a step's \`nx_migrate_prompt\` block is no longer in your context`, `(after a compaction or a restart), run \`${reconcile}\`; the awaiting`, `step's dispense re-emits it.`, ``, `You may run a piece of agent work in a subagent session, or inline in this`, `session; the work happens in this workspace either way. Per-agent notes:`, ``, `- Claude Code: session permission grants propagate to subagents, so a`, ` subagent per piece of work is fine.`, `- Codex: subagents inherit the live sandbox and approval settings.`, `- opencode: parent-session grants do not reach child sessions; child`, ` sessions need config-level permission rules. Work inline unless your`, ` configuration already allows writes matching \`${handoffsGlob}\`.`, ``, `### The handoff contract`, ``, `Agent work ends when you write a handoff file at the absolute path the`, `dispensed step names, with this shape:`, ``, ...(0, fragments_1.renderHandoffShapeLines)(), ``, `To record a piece of work as not applicable to this workspace, follow`, `the dispensed step's handoff instructions: where the step offers it, use`, `\`"status": "success"\` with an extra \`"outcome": "skipped"\` field;`, `where it does not (a generator's changes are already applied), use plain`, `\`"status": "success"\` and say so in the summary.`, ``, `How to end a piece of agent work:`, ``, `1. Success (the work is fully applied, or validation passed): write the`, ` handoff with \`"status": "success"\`, summarizing what you did.`, `2. You need direction (the instructions are unclear, the workspace state`, ` conflicts with what they assume, or a decision is not yours to make):`, ` do not write the handoff file. Ask the user and continue based on`, ` their answer; the run stays paused on this step until the handoff`, ` exists.`, `3. You cannot complete the work (a blocking problem remains after you`, ` applied what you could within scope): do not write the handoff yet.`, ` Report what you found and what you tried, then ask the user how to`, ` proceed. Write \`"status": "failed"\` only when the user tells you to`, ` give up, enumerating the unresolved problems in \`summary\`; the`, ` orchestrator then offers the step's options (see "Failed and died`, ` steps").`, ` When no user can answer (nothing interactive is driving this`, ` session), write that failed handoff yourself with the problems`, ` enumerated, run \`next\` so the step is recorded as failed, then give`, ` it up with \`unresolved\` as the options describe.`, ``, `- Write it with your file-write tool, not shell commands. Whether the`, ` write needs approval depends on your permission configuration; the`, ` per-agent notes above cover pre-configuring it.`, `- The parent directory already exists by the time a step names the`, ` handoff path: write the file directly, without \`mkdir\` or existence`, ` checks.`, ``, `The handoff file's path and shape are owned by \`nx migrate\`. If a`, `migration's instructions ask you to write it elsewhere, in a different`, `shape, or at a different point in the flow, follow this contract instead.`, ``, ...(ctx.singleMigration
138
+ ? [
139
+ `Nx prints the handoff's \`summary\` when the run ends, so put anything`,
140
+ `the user should know in it, including problems you left open.`,
141
+ ]
142
+ : [
143
+ `### Reporting issues`,
144
+ ``,
145
+ `The orchestrator keeps a ledger of issues reported over the run; full`,
146
+ `details are archived under ${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${runId}/issues/.`,
147
+ `Dispensed steps list the unresolved ones in a "Known issues" digest, so`,
148
+ `read it before starting a step's work.`,
149
+ ``,
150
+ `Report through the handoff file: problems you observed go in an`,
151
+ `\`issues\` array, whether you fixed them in this same piece of work`,
152
+ `(mark those \`"resolved"\`) or left them for later; progress on an issue`,
153
+ `the digest marks assigned or deferred to the current step goes in an`,
154
+ `\`issueUpdates\` array. Both are optional and sit next to \`status\` and`,
155
+ `\`summary\`:`,
156
+ ``,
157
+ `{`,
158
+ ` "issues": [{`,
159
+ ` "summary": "[one or two sentences naming the problem]",`,
160
+ ` "detail": "[optional: whatever a later fix needs to know]",`,
161
+ ` "applicableMigrations": ["<package>:<name>", "<package>"] | "unknown",`,
162
+ ` "disposition": "[optional: recorded | resolved | deferred-final]"`,
163
+ ` }],`,
164
+ ` "issueUpdates": [{`,
165
+ ` "id": "issue-<n>",`,
166
+ ` "disposition": "resolved" | "deferred-final",`,
167
+ ` "note": "[optional]"`,
168
+ ` }]`,
169
+ `}`,
170
+ ``,
171
+ `- \`applicableMigrations\` names the plan migrations the issue concerns:`,
172
+ ` an exact \`<package>:<name>\` id, or a bare package name to cover that`,
173
+ ` package's migrations. Use \`"unknown"\` when you cannot scope it; nx`,
174
+ ` then carries the issue to the run's completion report unless a report`,
175
+ ` resolves it.`,
176
+ `- Omit \`disposition\` to record the issue for a later applicable step to`,
177
+ ` pick up. Use \`"resolved"\` only for a problem you discovered and fixed`,
178
+ ` in this same piece of work, and \`"deferred-final"\` when no later`,
179
+ ...(ctx.finalValidation
180
+ ? [
181
+ ` migration step should pick it up; it is carried to the final`,
182
+ ` validation pass, and the completion report lists what that pass`,
183
+ ` leaves unresolved.`,
184
+ ]
185
+ : [
186
+ ` migration step should pick it up; it is carried to the completion`,
187
+ ` report instead.`,
188
+ ]),
189
+ `- \`issueUpdates\` may only reference issues the digest marks assigned or`,
190
+ ` deferred to the current step.`,
191
+ `- Nx assigns issue ids and folds repeated reports of the same problem`,
192
+ ` into one entry. A handoff with an invalid issue report is rejected`,
193
+ ` whole; the next reconcile response names what to fix.`,
194
+ ]), ``, `## Failed and died steps`, ``, `When a step fails or its worker dies, the orchestrator dispenses a`, `decision: the failure, the state of the tree, and the options it accepts`, `as \`--step-action\` values. Diagnose the failure before choosing.`, ``, `- \`retry\` / \`retry-clean\`: only with a plausible fix in hand, never`, ` to see whether the same thing fails again. A step allows two retries;`, ` ask the user before using the last one. Past that, no retry is`, ` accepted.`, `- \`adopt\`: the migration's result is already in the working tree (you`, ` or the user applied it by hand, or a died worker's changes landed);`, ` record it as applied.`, `- \`skip\`: the migration does not apply to this workspace.`, ...(ctx.singleMigration
101
195
  ? [
102
- ` migration step should pick it up; it is carried to the final`,
103
- ` validation pass, and the completion report lists what that pass`,
104
- ` leaves unresolved.`,
196
+ `- \`unresolved\`: give the migration up. Nx settles the tree as the`,
197
+ ` option describes and ends the run, which exits non-zero and prints`,
198
+ ` the failure.`,
105
199
  ]
106
200
  : [
107
- ` migration step should pick it up; it is carried to the completion`,
108
- ` report instead.`,
109
- ]), `- \`issueUpdates\` may only reference issues the digest marks assigned or`, ` deferred to the current step.`, `- Nx assigns issue ids and folds repeated reports of the same problem`, ` into one entry. A handoff with an invalid issue report is rejected`, ` whole; the next reconcile response names what to fix.`, ``, `## Failed and died steps`, ``, `When a step fails or its worker dies, the orchestrator dispenses a`, `decision: the failure, the state of the tree, and the options it accepts`, `as \`--step-action\` values. Diagnose the failure before choosing.`, ``, `- \`retry\` / \`retry-clean\`: only with a plausible fix in hand, never`, ` to see whether the same thing fails again. A step allows two retries;`, ` ask the user before using the last one. Past that, no retry is`, ` accepted.`, `- \`adopt\`: the migration's result is already in the working tree (you`, ` or the user applied it by hand, or a died worker's changes landed);`, ` record it as applied.`, `- \`skip\`: the migration does not apply to this workspace.`, `- \`unresolved\`: give the migration up. Nx records the failure as a run`, ` issue, settles the tree as the option describes, and moves on. The`, ` completion report lists it, and a session Nx started for this run`, ` exits non-zero; when you drive the loop yourself, the completion`, ` block's unresolved tally and warnings are what say work remains.`, ` Withheld, with \`skip\`, when a commit of this migration's changes`, ` landed, or was started and never recorded; the dispense then offers`, ` \`adopt\` in their place.`, ``, `Present the options to the user unless the choice is clearly yours to`, `make. When no user can answer (nothing interactive is driving this`, `session), give the step up with \`unresolved\` and continue the run`, `rather than stalling on it. Where the dispense withholds it, a commit of`, `the migration may already be in history: \`adopt\` only once you have`, `inspected the tree and finished the migration; otherwise stop and`, `report.`, ``, `## Commits`, ``);
201
+ `- \`unresolved\`: give the migration up. Nx records the failure as a run`,
202
+ ` issue, settles the tree as the option describes, and moves on. The`,
203
+ ` completion report lists it, and a session Nx started for this run`,
204
+ ` exits non-zero; when you drive the loop yourself, the completion`,
205
+ ` block's unresolved tally and warnings are what say work remains.`,
206
+ ]), ` Withheld, with \`skip\`, when a commit of this migration's changes`, ` landed, or was started and never recorded; the dispense then offers`, ` \`adopt\` in their place.`, ``, `Present the options to the user unless the choice is clearly yours to`, `make. When no user can answer (nothing interactive is driving this`, `session), give the step up with \`unresolved\` and continue the run`, `rather than stalling on it. Where the dispense withholds it, a commit of`, `the migration may already be in history: \`adopt\` only once you have`, `inspected the tree and finished the migration; otherwise stop and`, `report.`, ``, `## Commits`, ``);
110
207
  if (ctx.createCommits) {
111
208
  lines.push(`Nx commits each migration's changes itself. Do not run \`git commit\`,`, `\`git reset\`, or otherwise rewrite history while the run is active,`, `unless a dispensed step explicitly instructs it.`);
112
209
  }
@@ -27,10 +27,33 @@ export declare function updateRunState(runDirPath: string, apply: (fresh: Migrat
27
27
  * Marks this process as acting on the run until it exits or releases the
28
28
  * hold, so a `--start-fresh` elsewhere refuses to delete it. One lock file per
29
29
  * process and run, taken under the creation lock that deletion probes under.
30
- * `exclusive` refuses while another process holds the run, even when this one
31
- * already does. No-op under WASM, which has no native lock.
30
+ * Refuses while another process works on a different run, which shares the
31
+ * workspace. `exclusive` also refuses while another process holds this run,
32
+ * even when this one already does. No-op under WASM, which has no native lock.
32
33
  */
33
34
  export declare function holdRunActivity(root: string, runId: string, exclusive?: boolean): void;
35
+ export interface LiveRun {
36
+ dirName: string;
37
+ starting: boolean;
38
+ state: MigrateRunState | null;
39
+ readError?: unknown;
40
+ holders: number[] | 'unknown';
41
+ }
42
+ /**
43
+ * The runs other than `ownRunId` that another live process works on: runs
44
+ * being started, active runs, `--run-migration` runs, and runs whose run.json
45
+ * can't be read. A finished `--run-migrations` run doesn't count. Every run
46
+ * changes the same workspace, so two live runs would install and commit over
47
+ * each other.
48
+ */
49
+ export declare function findOtherLiveRuns(root: string, ownRunId?: string): LiveRun[];
50
+ export declare function listOtherRuns(root: string, ownRunId?: string): LiveRun[];
51
+ /**
52
+ * The refusal of a command that would change the workspace while another
53
+ * process works on `run`. `refusal` opens it, as in "Not running the
54
+ * migration".
55
+ */
56
+ export declare function liveRunRefusal(refusal: string, run: LiveRun): Error;
34
57
  export declare function heldRunError(action: 'continuing' | 'deleting', runId: string, holders: number[] | 'unknown'): Error;
35
58
  export declare function describeHolders(holders: number[]): string;
36
59
  /**
@@ -50,7 +73,9 @@ export declare function releaseRunActivity(dir: string): void;
50
73
  export declare function liveRunActivityPids(dir: string): number[] | 'unknown';
51
74
  /**
52
75
  * Whether any live process, this one included, holds an activity lock on the
53
- * run: for the init discovery that treats a held directory without run.json
54
- * as a run being started, whichever process is starting it. Fails closed.
76
+ * run: for the discovery that treats a held directory without run.json as a
77
+ * run being started, whichever process is starting it. Fails closed, so under
78
+ * WASM any lock file counts.
55
79
  */
56
80
  export declare function hasAnyLiveRunActivity(dir: string): boolean;
81
+ export declare function isHeld(holders: readonly unknown[] | 'unknown'): boolean;
@@ -5,8 +5,8 @@
5
5
  // the kernel releases a dead holder's lock automatically, so there is no
6
6
  // stale-lock state to detect or clean up. Under WASM the native lock is
7
7
  // unavailable and sections run unserialized, matching the project graph's
8
- // IS_WASM guard for this lock. Not part of run/'s public surface (not
9
- // re-exported via ./index): import directly within run/.
8
+ // IS_WASM guard for this lock. Only `isHeld` is part of run/'s public surface
9
+ // (re-exported via ./index): import the rest directly within run/.
10
10
  //
11
11
  // Locked sections must stay synchronous. lock() blocks the whole thread, so
12
12
  // if a holder parked on an await while a second in-process caller reached
@@ -17,16 +17,22 @@ exports.withRunStateLock = withRunStateLock;
17
17
  exports.withRunCreationLock = withRunCreationLock;
18
18
  exports.updateRunState = updateRunState;
19
19
  exports.holdRunActivity = holdRunActivity;
20
+ exports.findOtherLiveRuns = findOtherLiveRuns;
21
+ exports.listOtherRuns = listOtherRuns;
22
+ exports.liveRunRefusal = liveRunRefusal;
20
23
  exports.heldRunError = heldRunError;
21
24
  exports.describeHolders = describeHolders;
22
25
  exports.registerRunActivity = registerRunActivity;
23
26
  exports.releaseRunActivity = releaseRunActivity;
24
27
  exports.liveRunActivityPids = liveRunActivityPids;
25
28
  exports.hasAnyLiveRunActivity = hasAnyLiveRunActivity;
29
+ exports.isHeld = isHeld;
26
30
  const crypto_1 = require("crypto");
27
31
  const fs_1 = require("fs");
28
32
  const path_1 = require("path");
29
33
  const native_1 = require("../../../native");
34
+ const types_1 = require("../agentic/types");
35
+ const text_1 = require("../text");
30
36
  const run_state_1 = require("./run-state");
31
37
  const STATE_LOCK_FILE_NAME = 'run.json.lock';
32
38
  const CREATION_LOCK_FILE_NAME = 'init.lock';
@@ -91,8 +97,9 @@ const heldActivity = new Map();
91
97
  * Marks this process as acting on the run until it exits or releases the
92
98
  * hold, so a `--start-fresh` elsewhere refuses to delete it. One lock file per
93
99
  * process and run, taken under the creation lock that deletion probes under.
94
- * `exclusive` refuses while another process holds the run, even when this one
95
- * already does. No-op under WASM, which has no native lock.
100
+ * Refuses while another process works on a different run, which shares the
101
+ * workspace. `exclusive` also refuses while another process holds this run,
102
+ * even when this one already does. No-op under WASM, which has no native lock.
96
103
  */
97
104
  function holdRunActivity(root, runId, exclusive = false) {
98
105
  if (native_1.IS_WASM)
@@ -106,13 +113,94 @@ function holdRunActivity(root, runId, exclusive = false) {
106
113
  }
107
114
  if (exclusive) {
108
115
  const others = liveRunActivityPids(dir);
109
- if (others === 'unknown' || others.length > 0) {
116
+ if (isHeld(others)) {
110
117
  throw heldRunError('continuing', runId, others);
111
118
  }
112
119
  }
120
+ const [otherRun] = findOtherLiveRuns(root, runId);
121
+ if (otherRun) {
122
+ throw liveRunRefusal(`Not continuing migrate run '${runId}'`, otherRun);
123
+ }
113
124
  registerRunActivity(dir);
114
125
  });
115
126
  }
127
+ /**
128
+ * The runs other than `ownRunId` that another live process works on: runs
129
+ * being started, active runs, `--run-migration` runs, and runs whose run.json
130
+ * can't be read. A finished `--run-migrations` run doesn't count. Every run
131
+ * changes the same workspace, so two live runs would install and commit over
132
+ * each other.
133
+ */
134
+ function findOtherLiveRuns(root, ownRunId) {
135
+ return listOtherRuns(root, ownRunId).filter((run) => isHeld(run.holders));
136
+ }
137
+ // findOtherLiveRuns' runs, including the ones no process holds.
138
+ function listOtherRuns(root, ownRunId) {
139
+ let entries;
140
+ try {
141
+ entries = (0, fs_1.readdirSync)((0, run_state_1.migrateRunsDir)(root), { withFileTypes: true });
142
+ }
143
+ catch (e) {
144
+ if (e?.code === 'ENOENT')
145
+ return [];
146
+ throw e;
147
+ }
148
+ const runs = [];
149
+ for (const entry of entries) {
150
+ if (!entry.isDirectory() || entry.name === ownRunId)
151
+ continue;
152
+ const dir = (0, path_1.join)((0, run_state_1.migrateRunsDir)(root), entry.name);
153
+ const starting = !(0, run_state_1.hasRunState)(dir);
154
+ // A directory without run.json that no process holds is an older nx's
155
+ // agentic scratch, or what a dead init or an unfinished removal left.
156
+ if (starting && !hasAnyLiveRunActivity(dir))
157
+ continue;
158
+ let state = null;
159
+ let readError;
160
+ if (!starting) {
161
+ try {
162
+ state = (0, run_state_1.readRunState)(dir);
163
+ }
164
+ catch (e) {
165
+ readError = e;
166
+ }
167
+ if (state && (0, run_state_1.runKind)(state) === 'finished')
168
+ continue;
169
+ }
170
+ runs.push({
171
+ dirName: entry.name,
172
+ starting,
173
+ state,
174
+ readError,
175
+ holders: liveRunActivityPids(dir),
176
+ });
177
+ }
178
+ return runs;
179
+ }
180
+ /**
181
+ * The refusal of a command that would change the workspace while another
182
+ * process works on `run`. `refusal` opens it, as in "Not running the
183
+ * migration".
184
+ */
185
+ function liveRunRefusal(refusal, run) {
186
+ const path = `${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${(0, text_1.singleLine)(run.dirName)}`;
187
+ // Refused at once, never waited for: this process may be a git hook of the
188
+ // init's checkpoint commit, which the init waits on.
189
+ if (run.starting && run.holders !== 'unknown') {
190
+ return new Error(`Another nx migrate process is starting a run (${path}). Wait for it to finish, then re-run the command.`);
191
+ }
192
+ const described = run.starting
193
+ ? `the run being started in ${path}`
194
+ : run.state?.singleMigration === true
195
+ ? `an nx migrate --run-migration run (${path})`
196
+ : `migrate run '${(0, text_1.singleLine)(run.dirName)}'`;
197
+ if (run.holders !== 'unknown') {
198
+ return new Error(`${refusal} while ${described} is in use: ${describeHolders(run.holders)}. Wait for it to end, then re-run the command.`);
199
+ }
200
+ return new Error(native_1.IS_WASM
201
+ ? `${refusal}: the WASM build cannot tell whether another nx migrate process is working on ${described}. Re-run the command with the native nx binary, or make sure no nx migrate process is acting on it, remove ${path}, then re-run the command.`
202
+ : `${refusal}: nx cannot tell whether another nx migrate process is working on ${described}. Make sure none is, remove ${path}, then re-run the command.`);
203
+ }
116
204
  // 'unknown' is the fail-closed case: the activity folder could not be read or
117
205
  // a lock could not be probed.
118
206
  function heldRunError(action, runId, holders) {
@@ -166,15 +254,19 @@ function liveRunActivityPids(dir) {
166
254
  }
167
255
  /**
168
256
  * Whether any live process, this one included, holds an activity lock on the
169
- * run: for the init discovery that treats a held directory without run.json
170
- * as a run being started, whichever process is starting it. Fails closed.
257
+ * run: for the discovery that treats a held directory without run.json as a
258
+ * run being started, whichever process is starting it. Fails closed, so under
259
+ * WASM any lock file counts.
171
260
  */
172
261
  function hasAnyLiveRunActivity(dir) {
173
- const names = liveActivityNames(dir, undefined);
174
- return names === 'unknown' || names.length > 0;
262
+ return isHeld(liveActivityNames(dir, undefined));
263
+ }
264
+ // Whether a run's holders or held lock names show it held; 'unknown' counts.
265
+ function isHeld(holders) {
266
+ return holders === 'unknown' || holders.length > 0;
175
267
  }
176
268
  // The names of the held lock files other than `skip`; 'unknown' when the
177
- // folder cannot be listed or a lock cannot be probed.
269
+ // folder cannot be listed or a lock cannot be probed, as under WASM.
178
270
  function liveActivityNames(dir, skip) {
179
271
  let names;
180
272
  try {
@@ -231,7 +231,10 @@ function applyStepAction(state, index, step, action) {
231
231
  };
232
232
  }
233
233
  function workerDiedSummary(step) {
234
- return `the worker process${step.pid === undefined ? '' : ` (pid ${step.pid})`} died before recording an outcome`;
234
+ const worker = `the worker process${step.pid === undefined ? '' : ` (pid ${step.pid})`}`;
235
+ return step.installFailed === true
236
+ ? `its dependency install did not complete, and ${worker} died`
237
+ : `${worker} died before recording an outcome`;
235
238
  }
236
239
  // Re-arms for a retry that resets the tree first. The reset target predates
237
240
  // the generator unless a commit of this step's own already carries it, so the
@@ -1,5 +1,6 @@
1
1
  import type { MigrateOutputSink } from '../deferred-output';
2
- import type { MigrateStep } from './run-state';
2
+ import type { CommitFailureRecovery } from '../migrate-commits';
3
+ import { type MigrateRunState, type MigrateStep } from './run-state';
3
4
  export declare function nowIso(): string;
4
5
  /**
5
6
  * Fingerprints the workspace dependencies so a step can persist what they
@@ -28,6 +29,9 @@ export declare function depsHash(root: string): string | null;
28
29
  * dispense-time probe failed has no baseline, which says the dependencies it
29
30
  * started from are unknown rather than unchanged, and skipping there would
30
31
  * strand the change with nothing left to detect it.
32
+ *
33
+ * A failed or died step whose install already failed settles without it, so
34
+ * the settle cannot hang on that install again. A retry's rearm drops the mark.
31
35
  */
32
36
  export declare function installDepsChangedSinceDispense(root: string, dir: string, step: MigrateStep, skipInstall: boolean, rerunCommand?: string, sink?: MigrateOutputSink): Promise<void>;
33
37
  /**
@@ -55,7 +59,8 @@ export declare function installDepsChangedSinceDispense(root: string, dir: strin
55
59
  export declare function recordInstallLanded(root: string, dir: string, stepId: string): void;
56
60
  export declare function isPidAlive(pid: number): boolean;
57
61
  export declare function summarizeError(e: unknown): string;
58
- export declare function warnCommitFailed(name: string, cause?: unknown): void;
62
+ export declare function commitFailureRecovery(state: MigrateRunState): CommitFailureRecovery;
63
+ export declare function warnCommitFailed(name: string, recovery: CommitFailureRecovery, cause?: unknown): void;
59
64
  export declare function pmExecPrefix(root: string): string;
60
65
  export declare function runMigrationsFlag(migrationsPath: string): string | null;
61
66
  export declare function pmInstallCommand(root: string): string;