@oisincoveney/pipeline 3.24.2 → 3.24.3

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 (326) hide show
  1. package/README.md +2 -2
  2. package/defaults/pipeline.yaml +23 -5
  3. package/defaults/profiles.yaml +104 -21
  4. package/dist/argo-graph.js +88 -88
  5. package/dist/argo-submit.d.ts +109 -137
  6. package/dist/argo-submit.js +337 -360
  7. package/dist/argo-workflow.d.ts +156 -176
  8. package/dist/argo-workflow.js +96 -138
  9. package/dist/backlog.js +5 -4
  10. package/dist/bench/eval-report.js +21 -12
  11. package/dist/claude-settings-config.js +23 -31
  12. package/dist/claude-user-config.js +6 -16
  13. package/dist/cli/bootstrap-commands.js +3 -3
  14. package/dist/cli/doctor.d.ts +1 -1
  15. package/dist/cli/doctor.js +172 -169
  16. package/dist/cli/factory-commands.js +12 -11
  17. package/dist/cli/format.js +92 -86
  18. package/dist/cli/loop-commands.js +21 -21
  19. package/dist/cli/mcp-gateway-commands.js +12 -12
  20. package/dist/cli/plan-commands.js +85 -116
  21. package/dist/cli/program.d.ts +2 -2
  22. package/dist/cli/program.js +35 -43
  23. package/dist/cli/run-command.js +25 -25
  24. package/dist/cli/run-commands.js +25 -25
  25. package/dist/cli/run-resolver.d.ts +1 -1
  26. package/dist/cli/run-resolver.js +30 -32
  27. package/dist/cli/run-service.d.ts +2 -2
  28. package/dist/cli/run-service.js +226 -239
  29. package/dist/cli/submit-options.js +78 -82
  30. package/dist/cluster-doctor.js +147 -175
  31. package/dist/codex-config.js +24 -30
  32. package/dist/commands/bench-command.js +9 -7
  33. package/dist/commands/pipeline-command.js +9 -5
  34. package/dist/commands/runner-command-command.js +28 -18
  35. package/dist/commands/ticket/complete.js +29 -33
  36. package/dist/commands/ticket/create.js +82 -82
  37. package/dist/commands/ticket/graph-check.js +11 -11
  38. package/dist/commands/ticket/next.js +36 -36
  39. package/dist/commands/ticket/sequence.js +10 -12
  40. package/dist/commands/ticket/shared.d.ts +1 -1
  41. package/dist/commands/ticket/shared.js +63 -66
  42. package/dist/commands/ticket/start.js +58 -67
  43. package/dist/commands/ticket-command.js +2 -2
  44. package/dist/config/defaults.d.ts +139 -139
  45. package/dist/config/defaults.js +81 -80
  46. package/dist/config/lint.js +52 -64
  47. package/dist/config/load.d.ts +4 -4
  48. package/dist/config/load.js +62 -74
  49. package/dist/config/schema/catalog.js +1 -1
  50. package/dist/config/schema/mcp.js +46 -52
  51. package/dist/config/schemas.d.ts +375 -525
  52. package/dist/config/schemas.js +397 -397
  53. package/dist/config/validate.d.ts +1 -1
  54. package/dist/config/validate.js +236 -201
  55. package/dist/context/repo-map.js +102 -119
  56. package/dist/credentials/broker.d.ts +6 -8
  57. package/dist/credentials/broker.js +17 -16
  58. package/dist/credentials/codex-config.js +15 -19
  59. package/dist/credentials/file-targets.js +9 -15
  60. package/dist/credentials/local-codex-auth-sync.js +55 -58
  61. package/dist/credentials/opencode-config.js +28 -32
  62. package/dist/credentials/runner.js +13 -13
  63. package/dist/effect-sync-errors.js +28 -0
  64. package/dist/factory/create-experiment.js +38 -42
  65. package/dist/factory/exec.js +10 -12
  66. package/dist/factory/git-credentials.js +4 -4
  67. package/dist/factory/stamp-answers.js +10 -11
  68. package/dist/factory/template-update.js +65 -44
  69. package/dist/factory-lane.d.ts +26 -40
  70. package/dist/factory-lane.js +26 -25
  71. package/dist/gates.js +3 -3
  72. package/dist/git-remote-url.js +17 -19
  73. package/dist/hooks.d.ts +26 -25
  74. package/dist/hooks.js +20 -22
  75. package/dist/index.d.ts +1 -1
  76. package/dist/index.js +21 -35
  77. package/dist/install-commands/claude-code.js +96 -92
  78. package/dist/install-commands/host-selection.js +3 -6
  79. package/dist/install-commands/opencode.js +263 -259
  80. package/dist/install-commands/planner.js +159 -180
  81. package/dist/install-commands/result-format.js +1 -3
  82. package/dist/install-commands/shared.js +25 -42
  83. package/dist/install-commands/writer.js +22 -25
  84. package/dist/install-commands.js +8 -8
  85. package/dist/jscpd-output.js +29 -0
  86. package/dist/json-config-merge.js +14 -20
  87. package/dist/json-line-values.js +17 -6
  88. package/dist/loop/argo-poll.js +52 -71
  89. package/dist/loop/backlog-records.js +1 -3
  90. package/dist/loop/controller-deps.js +146 -159
  91. package/dist/loop/controller.js +162 -186
  92. package/dist/loop/gh-checks.js +62 -61
  93. package/dist/loop/gh-runner.js +22 -25
  94. package/dist/loop/loop-command.js +66 -58
  95. package/dist/loop/loop-controller-entrypoint.js +28 -30
  96. package/dist/loop/merge.js +36 -40
  97. package/dist/mcp/gateway-config.js +25 -29
  98. package/dist/mcp/gateway-doctor.js +104 -116
  99. package/dist/mcp/gateway-reconcile.js +17 -22
  100. package/dist/mcp/gateway-runtime.js +1 -3
  101. package/dist/mcp/host-config.js +37 -40
  102. package/dist/mcp/host-renderers.js +10 -18
  103. package/dist/mcp/repo-local-backends.js +50 -54
  104. package/dist/mcp/toolhive-vmcp.js +39 -39
  105. package/dist/model-resolver.js +29 -33
  106. package/dist/moka-global-config.d.ts +78 -54
  107. package/dist/moka-global-config.js +90 -92
  108. package/dist/moka-submit.d.ts +2163 -864
  109. package/dist/moka-submit.js +97 -99
  110. package/dist/opencode-project-config.js +34 -40
  111. package/dist/package-assets.js +1 -3
  112. package/dist/path-refs.js +2 -2
  113. package/dist/pipeline-init.js +25 -25
  114. package/dist/pipeline-runtime.d.ts +11 -11
  115. package/dist/pipeline-runtime.js +49 -50
  116. package/dist/planned-node.js +6 -3
  117. package/dist/planning/compile.d.ts +3 -3
  118. package/dist/planning/compile.js +140 -139
  119. package/dist/planning/dependency-refs.js +5 -5
  120. package/dist/planning/generate.d.ts +1293 -1293
  121. package/dist/planning/generate.js +305 -314
  122. package/dist/planning/graph.js +131 -149
  123. package/dist/remote/argo/model.d.ts +103 -121
  124. package/dist/remote/argo/model.js +205 -197
  125. package/dist/remote/argo/policy.js +39 -47
  126. package/dist/remote/argo/storage.js +81 -85
  127. package/dist/remote/submit/argo-submission.js +53 -58
  128. package/dist/remote/submit/compilation.js +46 -49
  129. package/dist/remote/submit/event-boundary.js +62 -76
  130. package/dist/remote/submit/hook-events.d.ts +5 -0
  131. package/dist/remote/submit/io.js +40 -43
  132. package/dist/remote/submit/options.d.ts +50 -0
  133. package/dist/remote/submit/options.js +27 -0
  134. package/dist/remote/submit/service.js +45 -49
  135. package/dist/run-control/command-context.js +6 -8
  136. package/dist/run-control/commands.js +10 -10
  137. package/dist/run-control/contracts.d.ts +40 -100
  138. package/dist/run-control/contracts.js +56 -69
  139. package/dist/run-control/detach.js +66 -72
  140. package/dist/run-control/file-errors.js +1 -3
  141. package/dist/run-control/logical-segment.js +3 -3
  142. package/dist/run-control/next-node.js +66 -78
  143. package/dist/run-control/postgres/postgres-run-control-store.js +195 -224
  144. package/dist/run-control/resume-command.js +33 -41
  145. package/dist/run-control/run-artifacts-command.js +83 -105
  146. package/dist/run-control/run-command-domain.js +7 -11
  147. package/dist/run-control/run-control-store.js +9 -9
  148. package/dist/run-control/run-query-command.js +73 -100
  149. package/dist/run-control/run-record.js +2 -2
  150. package/dist/run-control/run-state-lock.js +5 -5
  151. package/dist/run-control/runtime-event-projection.js +38 -50
  152. package/dist/run-control/runtime-reporter.js +84 -102
  153. package/dist/run-control/stop-command.js +42 -53
  154. package/dist/run-control/store-fs-effects.js +3 -1
  155. package/dist/run-control/store-manifest.js +38 -13
  156. package/dist/run-control/store-paths.js +3 -3
  157. package/dist/run-control/store.js +1 -1
  158. package/dist/run-control/submit-result.js +21 -21
  159. package/dist/run-control/supervisor.js +77 -68
  160. package/dist/run-control/workspace.js +2 -2
  161. package/dist/run-state/git-refs.js +257 -307
  162. package/dist/runner/opencode-excludes.js +4 -4
  163. package/dist/runner/subprocess-result.js +28 -37
  164. package/dist/runner/subprocess.js +41 -41
  165. package/dist/runner/timeouts.js +14 -13
  166. package/dist/runner-command/dynamic-command.js +46 -39
  167. package/dist/runner-command/finalize.js +174 -153
  168. package/dist/runner-command/lifecycle-context.js +112 -130
  169. package/dist/runner-command/lifecycle.js +77 -71
  170. package/dist/runner-command/pre-schedule.js +201 -222
  171. package/dist/runner-command/run.js +432 -447
  172. package/dist/runner-command/schedule-source-options.js +8 -10
  173. package/dist/runner-command/select-ready-wave.js +38 -29
  174. package/dist/runner-command/task-descriptor.js +11 -17
  175. package/dist/runner-command-contract.d.ts +147 -156
  176. package/dist/runner-command-contract.js +401 -410
  177. package/dist/runner-event-schema.d.ts +364 -504
  178. package/dist/runner-event-schema.js +187 -175
  179. package/dist/runner-event-sink.js +37 -43
  180. package/dist/runner-output.js +6 -6
  181. package/dist/runner.d.ts +56 -36
  182. package/dist/runner.js +175 -127
  183. package/dist/runtime/actor-ids.js +2 -2
  184. package/dist/runtime/agent-node/agent-node.js +99 -111
  185. package/dist/runtime/agent-node/handoff-finalization.js +23 -24
  186. package/dist/runtime/agent-node/model-selection.js +19 -22
  187. package/dist/runtime/agent-node/output-finalization.js +174 -205
  188. package/dist/runtime/agent-node/prompt-rendering.js +207 -251
  189. package/dist/runtime/agent-node/session-execution.js +34 -38
  190. package/dist/runtime/builtins/builtins.js +212 -284
  191. package/dist/runtime/changed-files/changed-files.js +26 -34
  192. package/dist/runtime/command-executor/command-executor.js +31 -23
  193. package/dist/runtime/config-error.js +1 -3
  194. package/dist/runtime/context/context.js +35 -37
  195. package/dist/runtime/contracts/index.d.ts +1 -1
  196. package/dist/runtime/detached-race.js +11 -14
  197. package/dist/runtime/drain-merge/drain-merge.js +137 -163
  198. package/dist/runtime/durable-store/acquisition.js +5 -3
  199. package/dist/runtime/durable-store/durable-store.d.ts +1 -1
  200. package/dist/runtime/durable-store/postgres/migrate-substrate.js +36 -33
  201. package/dist/runtime/durable-store/postgres/postgres-store.js +17 -18
  202. package/dist/runtime/events/events.js +255 -323
  203. package/dist/runtime/gates/adjudication/llm-judge.js +21 -24
  204. package/dist/runtime/gates/adjudication/structured-claim.js +15 -16
  205. package/dist/runtime/gates/adjudicator/adjudicator.js +27 -27
  206. package/dist/runtime/gates/adjudicator/index.js +2 -2
  207. package/dist/runtime/gates/contract.js +5 -9
  208. package/dist/runtime/gates/gates.js +9 -8
  209. package/dist/runtime/gates/kinds/acceptance/acceptance.js +66 -70
  210. package/dist/runtime/gates/kinds/acceptance/index.js +2 -2
  211. package/dist/runtime/gates/kinds/artifact/artifact.js +4 -4
  212. package/dist/runtime/gates/kinds/artifact/index.js +2 -2
  213. package/dist/runtime/gates/kinds/builtin/builtin.js +4 -4
  214. package/dist/runtime/gates/kinds/builtin/index.js +2 -2
  215. package/dist/runtime/gates/kinds/changed-files/changed-files.js +31 -35
  216. package/dist/runtime/gates/kinds/changed-files/index.js +2 -2
  217. package/dist/runtime/gates/kinds/command/command.js +3 -3
  218. package/dist/runtime/gates/kinds/command/index.js +2 -2
  219. package/dist/runtime/gates/kinds/json-schema/index.js +2 -2
  220. package/dist/runtime/gates/kinds/json-schema/json-schema.js +7 -6
  221. package/dist/runtime/gates/kinds/verdict/index.js +2 -2
  222. package/dist/runtime/gates/kinds/verdict/verdict.js +3 -3
  223. package/dist/runtime/gates/orchestrator.js +72 -86
  224. package/dist/runtime/gates/registry.js +1 -3
  225. package/dist/runtime/handoff.d.ts +1 -1
  226. package/dist/runtime/handoff.js +43 -46
  227. package/dist/runtime/hooks/command-hook.js +57 -71
  228. package/dist/runtime/hooks/context.js +21 -25
  229. package/dist/runtime/hooks/events.js +84 -100
  230. package/dist/runtime/hooks/execution.js +6 -12
  231. package/dist/runtime/hooks/hooks.js +34 -23
  232. package/dist/runtime/hooks/invocation.js +23 -29
  233. package/dist/runtime/hooks/module-hook.js +35 -35
  234. package/dist/runtime/hooks/policy.js +32 -29
  235. package/dist/runtime/hooks/results.js +56 -66
  236. package/dist/runtime/journal-acquisition.d.ts +1 -1
  237. package/dist/runtime/journal-acquisition.js +14 -14
  238. package/dist/runtime/json-validation/json-validation.js +50 -72
  239. package/dist/runtime/local-scheduler.js +46 -48
  240. package/dist/runtime/node-execution.js +375 -446
  241. package/dist/runtime/node-protocol/node-protocol.js +36 -79
  242. package/dist/runtime/node-state-store.d.ts +1 -1
  243. package/dist/runtime/node-state-store.js +13 -57
  244. package/dist/runtime/node-state-tracker.js +46 -54
  245. package/dist/runtime/open-pull-request/open-pull-request.js +143 -187
  246. package/dist/runtime/opencode-adapter.js +23 -24
  247. package/dist/runtime/opencode-agent-name.js +6 -6
  248. package/dist/runtime/opencode-runtime.js +92 -92
  249. package/dist/runtime/opencode-server.js +39 -53
  250. package/dist/runtime/opencode-session-executor.js +315 -334
  251. package/dist/runtime/parallel-node/parallel-node.js +122 -134
  252. package/dist/runtime/parallel-worktrees/parallel-worktrees.js +90 -73
  253. package/dist/runtime/protected-paths/protected-paths.js +70 -71
  254. package/dist/runtime/remediation/remediation.js +202 -231
  255. package/dist/runtime/retry.js +8 -10
  256. package/dist/runtime/run-journal.js +7 -7
  257. package/dist/runtime/runtime-results.js +54 -69
  258. package/dist/runtime/scheduled-dependencies.d.ts +1 -1
  259. package/dist/runtime/scheduled-dependencies.js +59 -74
  260. package/dist/runtime/scheduler.js +177 -202
  261. package/dist/runtime/services/agent-node-runtime-service.js +3 -3
  262. package/dist/runtime/services/backlog-service.js +8 -14
  263. package/dist/runtime/services/command-executor-service.js +1 -1
  264. package/dist/runtime/services/config-io-service.js +24 -21
  265. package/dist/runtime/services/drain-merge-git-service.js +1 -1
  266. package/dist/runtime/services/file-system-service.js +9 -7
  267. package/dist/runtime/services/git-porcelain-service.js +6 -6
  268. package/dist/runtime/services/kubernetes-argo-service.d.ts +10 -2
  269. package/dist/runtime/services/kubernetes-argo-service.js +90 -84
  270. package/dist/runtime/services/mcp-gateway-service.js +82 -81
  271. package/dist/runtime/services/open-pull-request-git-service.js +1 -3
  272. package/dist/runtime/services/opencode-runtime-server-service.js +10 -12
  273. package/dist/runtime/services/opencode-sdk-service.js +7 -7
  274. package/dist/runtime/services/repo-io-service.js +10 -12
  275. package/dist/runtime/services/run-journal-file-service.js +2 -2
  276. package/dist/runtime/services/runner-command-io-service.js +48 -52
  277. package/dist/runtime/services/runner-event-sink-http-service.js +31 -39
  278. package/dist/runtime/step/step-node.js +15 -15
  279. package/dist/runtime/workflow-execution.js +55 -57
  280. package/dist/runtime/workflow-lifecycle.js +81 -101
  281. package/dist/safe-json.js +16 -16
  282. package/dist/schedule/backlog-context.js +120 -116
  283. package/dist/schedule/baseline.js +249 -255
  284. package/dist/schedule/passes/coverage.js +63 -68
  285. package/dist/schedule/passes/drain-merge.js +34 -40
  286. package/dist/schedule/passes/ids.js +15 -17
  287. package/dist/schedule/passes/models.js +40 -37
  288. package/dist/schedule/passes/open-pull-request.js +12 -18
  289. package/dist/schedule/passes/references.js +18 -22
  290. package/dist/schedule/prompts.js +134 -141
  291. package/dist/schedule/scheduling-roles.js +8 -14
  292. package/dist/schema-boundary.d.ts +1 -0
  293. package/dist/schema-boundary.js +145 -0
  294. package/dist/serialized-write-queue.js +2 -2
  295. package/dist/standard-output-schemas.js +61 -59
  296. package/dist/strings.js +12 -12
  297. package/dist/task-ref.js +5 -5
  298. package/dist/tickets/apply-ticket-plan.js +51 -61
  299. package/dist/tickets/backlog-task-store.d.ts +13 -22
  300. package/dist/tickets/backlog-task-store.js +80 -94
  301. package/dist/tickets/completion/complete-ticket.js +53 -59
  302. package/dist/tickets/ticket-graph-dto.d.ts +24 -47
  303. package/dist/tickets/ticket-graph-dto.js +40 -40
  304. package/dist/tickets/ticket-graph.d.ts +7 -1
  305. package/dist/tickets/ticket-graph.js +36 -50
  306. package/dist/tickets/ticket-plan-command-args.js +6 -10
  307. package/dist/tickets/ticket-plan-render.js +10 -16
  308. package/dist/tickets/ticket-plan.d.ts +31 -40
  309. package/dist/tickets/ticket-plan.js +97 -77
  310. package/dist/tickets/ticket-selection.d.ts +1 -1
  311. package/dist/tickets/ticket-selection.js +37 -52
  312. package/dist/tickets/ticket-task-index.js +13 -17
  313. package/dist/tickets/validation-error-format.js +3 -10
  314. package/dist/token-estimator.js +12 -7
  315. package/dist/unknown-fields.js +21 -0
  316. package/dist/workflow-submit-contract.d.ts +9 -9
  317. package/dist/workflow-submit-contract.js +10 -9
  318. package/docs/adr-opencode-first-goal-loop-runtime.md +11 -11
  319. package/docs/config-architecture.md +13 -13
  320. package/docs/mcp-host-isolation.md +1 -0
  321. package/docs/moka-orchestrator-design.md +3 -3
  322. package/docs/operator-guide.md +1 -1
  323. package/docs/runtime-actor-model.md +18 -18
  324. package/docs/slash-command-adapter-contract.md +3 -3
  325. package/package.json +73 -76
  326. package/dist/config/schema/reference-validation.js +0 -83
@@ -1,56 +1,53 @@
1
1
  import { createSerializedWriteQueue } from "../serialized-write-queue.js";
2
2
  import { withRunStateLock } from "./run-state-lock.js";
3
3
  import { createRuntimeEventProjectionState, projectRuntimeEvent } from "./runtime-event-projection.js";
4
- import { Effect } from "effect";
5
4
  import { isAbsolute, join } from "node:path";
5
+ import { Effect } from "effect";
6
6
  import { appendFile, mkdir } from "node:fs/promises";
7
7
  //#region src/run-control/runtime-reporter.ts
8
8
  const RUNS_DIRECTORY = ".pipeline/runs";
9
9
  const RUNTIME_EVENTS_FILE = "runtime-events.jsonl";
10
10
  const NODES_DIRECTORY = "nodes";
11
11
  const STDOUT_ARTIFACT = "stdout.jsonl";
12
- function createRunStoreRuntimeReporter(input) {
13
- return Effect.runSync(createRunStoreRuntimeReporterEffect(input));
14
- }
15
- function createRunStoreRuntimeReporterEffect(input) {
16
- return Effect.sync(() => createRunStoreRuntimeReporterRuntime(input));
17
- }
18
- function createRunStoreRuntimeReporterRuntime(input) {
19
- const now = input.now ?? (() => /* @__PURE__ */ new Date());
20
- const store = input.store;
21
- let projectionState = createRuntimeEventProjectionState();
22
- const writes = createSerializedWriteQueue();
23
- const enqueue = (event) => {
24
- const projection = projectRuntimeEvent(event, projectionState);
25
- projectionState = projection.state;
26
- const persisted = persistRuntimeEventEffect(input, store, event, projection.writes, now).pipe(Effect.catch((error) => warnPersistSkipped(input, event, error)));
27
- writes.enqueue(() => withRunStateLock(() => Effect.runPromise(persisted)));
28
- };
29
- const flushEffect = () => Effect.tryPromise({
30
- catch: (error) => error,
31
- try: () => writes.flush()
32
- });
33
- return {
34
- flush: () => Effect.runPromise(flushEffect()),
35
- flushEffect,
36
- reporter(event) {
37
- enqueue(event);
38
- input.reporter?.(event);
39
- }
40
- };
41
- }
42
- function persistRuntimeEventEffect(input, store, event, writeIntents, now) {
43
- return Effect.gen(function* () {
44
- const writesFilesystemObservability = writesFilesystemRunState(store, input.runId);
45
- if (writesFilesystemObservability) yield* appendRuntimeEventEffect(input, event);
46
- for (const writeIntent of writeIntents) yield* persistStoreWriteIntentEffect(input, store, writeIntent, now);
47
- if (writesFilesystemObservability && event.type === "node.output.recorded") yield* appendNodeStdoutEffect(input, event.nodeId, event);
48
- });
49
- }
50
- function writesFilesystemRunState(store, runId) {
51
- return store.statusPaths({ runId }).manifest.startsWith(`${RUNS_DIRECTORY}/`);
52
- }
53
- function persistStoreWriteIntentEffect(input, store, writeIntent, now) {
12
+ const writesFilesystemRunState = (store, runId) => store.statusPaths({ runId }).manifest.startsWith(`${RUNS_DIRECTORY}/`);
13
+ const appendJsonlEffect = (path, value) => Effect.tryPromise({
14
+ catch: (error) => error,
15
+ try: async () => {
16
+ await appendFile(path, `${JSON.stringify(value)}\n`, "utf-8");
17
+ }
18
+ });
19
+ const mkdirEffect = (path, options) => Effect.tryPromise({
20
+ catch: (error) => error,
21
+ try: async () => await mkdir(path, options)
22
+ }).pipe(Effect.asVoid);
23
+ const logicalSegment = (label, value) => {
24
+ if (value.length === 0 || isAbsolute(value) || value.includes("/") || value.includes("\\") || value === "." || value === "..") throw new Error(`${label} must be a non-empty logical identifier.`);
25
+ return value;
26
+ };
27
+ const runDirectory = (workspaceRoot, runId) => join(workspaceRoot, RUNS_DIRECTORY, logicalSegment("runId", runId));
28
+ const runDirectoryEffect = (workspaceRoot, runId) => Effect.try({
29
+ catch: (error) => error,
30
+ try: () => runDirectory(workspaceRoot, runId)
31
+ });
32
+ const appendRuntimeEventEffect = (input, event) => Effect.gen(function* effectBody() {
33
+ const runRoot = yield* runDirectoryEffect(input.workspaceRoot, input.runId);
34
+ yield* mkdirEffect(runRoot, { recursive: true });
35
+ yield* appendJsonlEffect(join(runRoot, RUNTIME_EVENTS_FILE), event);
36
+ });
37
+ const logicalSegmentEffect = (label, value) => Effect.try({
38
+ catch: (error) => error,
39
+ try: () => logicalSegment(label, value)
40
+ });
41
+ const appendNodeStdoutEffect = (input, nodeId, event) => Effect.gen(function* effectBody() {
42
+ const nodeRoot = join(yield* runDirectoryEffect(input.workspaceRoot, input.runId), NODES_DIRECTORY, yield* logicalSegmentEffect("nodeId", nodeId));
43
+ yield* mkdirEffect(nodeRoot, { recursive: true });
44
+ yield* appendJsonlEffect(join(nodeRoot, STDOUT_ARTIFACT), event);
45
+ });
46
+ const timestamp = (now) => now().toISOString();
47
+ const assertNever = (value) => {
48
+ throw new Error(`Unhandled runtime reporter value: ${String(value)}`);
49
+ };
50
+ const persistStoreWriteIntentEffect = (input, store, writeIntent, now) => {
54
51
  switch (writeIntent.type) {
55
52
  case "node.session": return store.updateNodeSession({
56
53
  nodeId: writeIntent.nodeId,
@@ -70,66 +67,51 @@ function persistStoreWriteIntentEffect(input, store, writeIntent, now) {
70
67
  });
71
68
  default: return assertNever(writeIntent);
72
69
  }
73
- }
74
- function appendRuntimeEventEffect(input, event) {
75
- return Effect.gen(function* () {
76
- const runRoot = yield* runDirectoryEffect(input.workspaceRoot, input.runId);
77
- yield* mkdirEffect(runRoot, { recursive: true });
78
- yield* appendJsonlEffect(join(runRoot, RUNTIME_EVENTS_FILE), event);
79
- });
80
- }
81
- function appendNodeStdoutEffect(input, nodeId, event) {
82
- return Effect.gen(function* () {
83
- const nodeRoot = join(yield* runDirectoryEffect(input.workspaceRoot, input.runId), NODES_DIRECTORY, yield* logicalSegmentEffect("nodeId", nodeId));
84
- yield* mkdirEffect(nodeRoot, { recursive: true });
85
- yield* appendJsonlEffect(join(nodeRoot, STDOUT_ARTIFACT), event);
86
- });
87
- }
88
- function appendJsonlEffect(path, value) {
89
- return Effect.tryPromise({
90
- catch: (error) => error,
91
- try: () => appendFile(path, `${JSON.stringify(value)}\n`, "utf8")
92
- });
93
- }
94
- function mkdirEffect(path, options) {
95
- return Effect.tryPromise({
96
- catch: (error) => error,
97
- try: () => mkdir(path, options)
98
- }).pipe(Effect.asVoid);
99
- }
100
- function runDirectory(workspaceRoot, runId) {
101
- return join(workspaceRoot, RUNS_DIRECTORY, logicalSegment("runId", runId));
102
- }
103
- function runDirectoryEffect(workspaceRoot, runId) {
104
- return Effect.try({
105
- catch: (error) => error,
106
- try: () => runDirectory(workspaceRoot, runId)
107
- });
108
- }
109
- function logicalSegment(label, value) {
110
- if (value.length === 0 || isAbsolute(value) || value.includes("/") || value.includes("\\") || value === "." || value === "..") throw new Error(`${label} must be a non-empty logical identifier.`);
111
- return value;
112
- }
113
- function logicalSegmentEffect(label, value) {
114
- return Effect.try({
70
+ };
71
+ const persistRuntimeEventEffect = (input, store, event, writeIntents, now) => Effect.gen(function* effectBody() {
72
+ const writesFilesystemObservability = writesFilesystemRunState(store, input.runId);
73
+ if (writesFilesystemObservability) yield* appendRuntimeEventEffect(input, event);
74
+ for (const writeIntent of writeIntents) yield* persistStoreWriteIntentEffect(input, store, writeIntent, now);
75
+ if (writesFilesystemObservability && event.type === "node.output.recorded") yield* appendNodeStdoutEffect(input, event.nodeId, event);
76
+ });
77
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
78
+ const warnPersistSkipped = (input, event, error) => Effect.sync(() => {
79
+ const nodeId = "nodeId" in event && typeof event.nodeId === "string" ? ` node=${event.nodeId}` : "";
80
+ process.stderr.write(`run-control: skipped persisting ${event.type}${nodeId} for run ${input.runId}: ${errorMessage(error)}\n`);
81
+ });
82
+ const createRunStoreRuntimeReporterRuntime = (input) => {
83
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
84
+ const { store } = input;
85
+ let projectionState = createRuntimeEventProjectionState();
86
+ const writes = createSerializedWriteQueue();
87
+ const enqueue = (event) => {
88
+ const projection = projectRuntimeEvent(event, projectionState);
89
+ projectionState = projection.state;
90
+ const persisted = persistRuntimeEventEffect(input, store, event, projection.writes, now).pipe(Effect.catch((error) => warnPersistSkipped(input, event, error)));
91
+ writes.enqueue(async () => {
92
+ await withRunStateLock(async () => {
93
+ await Effect.runPromise(persisted);
94
+ });
95
+ });
96
+ };
97
+ const flushEffect = () => Effect.tryPromise({
115
98
  catch: (error) => error,
116
- try: () => logicalSegment(label, value)
117
- });
118
- }
119
- function timestamp(now) {
120
- return now().toISOString();
121
- }
122
- function assertNever(value) {
123
- throw new Error(`Unhandled runtime reporter value: ${String(value)}`);
124
- }
125
- function warnPersistSkipped(input, event, error) {
126
- return Effect.sync(() => {
127
- const nodeId = "nodeId" in event && typeof event.nodeId === "string" ? ` node=${event.nodeId}` : "";
128
- process.stderr.write(`run-control: skipped persisting ${event.type}${nodeId} for run ${input.runId}: ${errorMessage(error)}\n`);
99
+ try: async () => {
100
+ await writes.flush();
101
+ }
129
102
  });
130
- }
131
- function errorMessage(error) {
132
- return error instanceof Error ? error.message : String(error);
133
- }
103
+ return {
104
+ flush: async () => {
105
+ await Effect.runPromise(flushEffect());
106
+ },
107
+ flushEffect,
108
+ reporter(event) {
109
+ enqueue(event);
110
+ input.reporter?.(event);
111
+ }
112
+ };
113
+ };
114
+ const createRunStoreRuntimeReporterEffect = (input) => Effect.sync(() => createRunStoreRuntimeReporterRuntime(input));
115
+ const createRunStoreRuntimeReporter = (input) => Effect.runSync(createRunStoreRuntimeReporterEffect(input));
134
116
  //#endregion
135
117
  export { createRunStoreRuntimeReporter, createRunStoreRuntimeReporterEffect };
@@ -1,60 +1,49 @@
1
1
  import { ACTIVE_NODE_STATUSES, requireKnownNodeEffect } from "./run-command-domain.js";
2
2
  import { requireRunEffect } from "./run-query-command.js";
3
- import { Effect } from "effect";
3
+ import { Effect, Option } from "effect";
4
4
  //#region src/run-control/stop-command.ts
5
- function stopRunOrNodeEffect(input) {
6
- return Effect.gen(function* () {
7
- const run = yield* requireRunEffect(input.store, input.runId);
8
- const at = (/* @__PURE__ */ new Date()).toISOString();
9
- if (input.nodeId) return yield* stopNodeEffect(input.store, run, input.nodeId, at);
10
- return yield* stopRunEffect(input.store, run, at);
5
+ const stopNodeEffect = (store, run, requestedNodeId, at) => Effect.gen(function* effectBody() {
6
+ const nodeId = yield* requireKnownNodeEffect(run, requestedNodeId);
7
+ yield* store.updateNodeStatus({
8
+ at,
9
+ nodeId,
10
+ runId: run.runId,
11
+ status: "aborted"
11
12
  });
12
- }
13
- function stopNodeEffect(store, run, requestedNodeId, at) {
14
- return Effect.gen(function* () {
15
- const nodeId = yield* requireKnownNodeEffect(run, requestedNodeId);
16
- yield* store.updateNodeStatus({
17
- at,
18
- nodeId,
19
- runId: run.runId,
20
- status: "aborted"
21
- });
22
- return `Run ${run.runId} node ${nodeId} aborted.`;
13
+ return `Run ${run.runId} node ${nodeId} aborted.`;
14
+ });
15
+ const activeNodeIds = (run) => Object.entries(run.nodes).filter(([, status]) => ACTIVE_NODE_STATUSES.has(status)).map(([nodeId]) => nodeId);
16
+ const isNoSuchProcess = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
17
+ const stopControllerProcessEffect = (pid) => Effect.sync(() => {
18
+ if (Option.isNone(pid) || pid.value === 0) return;
19
+ try {
20
+ process.kill(-pid.value, "SIGTERM");
21
+ } catch (error) {
22
+ if (isNoSuchProcess(error)) return;
23
+ process.kill(pid.value, "SIGTERM");
24
+ }
25
+ });
26
+ const stopRunEffect = (store, run, at) => Effect.gen(function* effectBody() {
27
+ yield* stopControllerProcessEffect(Option.fromNullishOr(run.controller?.pid));
28
+ yield* store.updateRunStatus({
29
+ at,
30
+ runId: run.runId,
31
+ status: "aborted"
23
32
  });
24
- }
25
- function stopRunEffect(store, run, at) {
26
- return Effect.gen(function* () {
27
- yield* stopControllerProcessEffect(run.controller?.pid);
28
- yield* store.updateRunStatus({
29
- at,
30
- runId: run.runId,
31
- status: "aborted"
32
- });
33
- yield* Effect.forEach(activeNodeIds(run), (nodeId) => store.updateNodeStatus({
34
- at,
35
- nodeId,
36
- runId: run.runId,
37
- status: "aborted"
38
- }));
39
- return `Run ${run.runId} aborted.`;
40
- });
41
- }
42
- function activeNodeIds(run) {
43
- return Object.entries(run.nodes).filter(([, status]) => ACTIVE_NODE_STATUSES.has(status)).map(([nodeId]) => nodeId);
44
- }
45
- function stopControllerProcessEffect(pid) {
46
- return Effect.sync(() => {
47
- if (!pid) return;
48
- try {
49
- process.kill(-pid, "SIGTERM");
50
- } catch (error) {
51
- if (isNoSuchProcess(error)) return;
52
- process.kill(pid, "SIGTERM");
53
- }
54
- });
55
- }
56
- function isNoSuchProcess(error) {
57
- return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
58
- }
33
+ yield* Effect.forEach(activeNodeIds(run), (nodeId) => store.updateNodeStatus({
34
+ at,
35
+ nodeId,
36
+ runId: run.runId,
37
+ status: "aborted"
38
+ }));
39
+ return `Run ${run.runId} aborted.`;
40
+ });
41
+ const stopRunOrNodeEffect = (input) => Effect.gen(function* effectBody() {
42
+ const run = yield* requireRunEffect(input.store, input.runId);
43
+ const at = (/* @__PURE__ */ new Date()).toISOString();
44
+ const nodeId = Option.fromNullishOr(input.nodeId).pipe(Option.filter((value) => value.length > 0));
45
+ if (Option.isSome(nodeId)) return yield* stopNodeEffect(input.store, run, nodeId.value, at);
46
+ return yield* stopRunEffect(input.store, run, at);
47
+ });
59
48
  //#endregion
60
49
  export { stopRunOrNodeEffect };
@@ -1,3 +1,5 @@
1
- import "effect";
1
+ import { Option } from "effect";
2
2
  import "node:fs/promises";
3
+ Option.none();
4
+ //#endregion
3
5
  export {};
@@ -1,28 +1,53 @@
1
+ import { requiredString, struct } from "../schema-boundary.js";
1
2
  import { mokaNodeStatusSchema, mokaRunStatusSchema, parseMokaRunManifest } from "./contracts.js";
2
3
  import { parseLogicalSegment } from "./logical-segment.js";
3
- import { z } from "zod";
4
+ import "effect/Option";
5
+ import * as R from "effect/Record";
6
+ import * as Schema from "effect/Schema";
7
+ import * as Arr from "effect/Array";
8
+ import "effect/Match";
9
+ import * as Str from "effect/String";
4
10
  //#region src/run-control/store-manifest.ts
5
- const runStatusNodeSchema = z.union([mokaNodeStatusSchema, z.object({
6
- sessionId: z.string().min(1).optional(),
11
+ const runStatusNode = Schema.Union([mokaNodeStatusSchema, struct({
12
+ sessionId: Schema.optional(requiredString),
7
13
  status: mokaNodeStatusSchema
8
- }).strict()]);
9
- z.object({
10
- nodes: z.record(z.string().min(1), runStatusNodeSchema),
14
+ })]);
15
+ struct({
16
+ nodes: Schema.Record(requiredString, runStatusNode),
11
17
  status: mokaRunStatusSchema
12
- }).strict();
13
- z.object({ sessionId: z.string().min(1) }).passthrough();
14
- function publishScheduleManifest(input) {
15
- if (input.schedule.length === 0) throw new Error("schedule must be a non-empty string.");
16
- if (input.manifest.schedule !== void 0 && input.manifest.schedule !== input.schedule) throw new Error(`Run ${input.manifest.runId} already has a different published schedule.`);
18
+ });
19
+ struct({ sessionId: requiredString });
20
+ var EmptyPublishedScheduleError = class extends Schema.TaggedErrorClass()("EmptyPublishedScheduleError", { message: Schema.String }) {
21
+ constructor() {
22
+ super({ message: "schedule must be a non-empty string." });
23
+ }
24
+ };
25
+ var PublishedScheduleConflictError = class extends Schema.TaggedErrorClass()("PublishedScheduleConflictError", {
26
+ message: Schema.String,
27
+ runId: requiredString
28
+ }) {
29
+ constructor(runId) {
30
+ super({
31
+ message: `Run ${runId} already has a different published schedule.`,
32
+ runId
33
+ });
34
+ }
35
+ };
36
+ const queuedNodeEntry = (nodeId) => [nodeId, "queued"];
37
+ const queuedNodeRecord = (nodeIds) => R.fromEntries(Arr.map(nodeIds, queuedNodeEntry));
38
+ const publishScheduleManifest = (input) => {
39
+ if (Str.isEmpty(input.schedule)) throw new EmptyPublishedScheduleError();
40
+ if (input.manifest.schedule !== void 0 && input.manifest.schedule !== input.schedule) throw new PublishedScheduleConflictError(input.manifest.runId);
17
41
  const nodeIds = input.nodeIds.map((nodeId) => parseLogicalSegment("nodeId", nodeId));
42
+ const missingNodes = Arr.filter(nodeIds, (nodeId) => !R.has(input.manifest.nodes, nodeId));
18
43
  return parseMokaRunManifest({
19
44
  ...input.manifest,
20
45
  nodes: {
21
46
  ...input.manifest.nodes,
22
- ...Object.fromEntries(nodeIds.filter((nodeId) => !(nodeId in input.manifest.nodes)).map((nodeId) => [nodeId, "queued"]))
47
+ ...queuedNodeRecord(missingNodes)
23
48
  },
24
49
  schedule: input.schedule
25
50
  });
26
- }
51
+ };
27
52
  //#endregion
28
53
  export { publishScheduleManifest };
@@ -1,13 +1,13 @@
1
1
  import "./logical-segment.js";
2
- import "effect";
3
2
  import { join } from "node:path";
3
+ import "effect";
4
4
  //#region src/run-control/store-paths.ts
5
5
  const RUNS_DIRECTORY = ".pipeline/runs";
6
6
  const MANIFEST_FILE = "manifest.json";
7
7
  const STATUS_FILE = "status.json";
8
8
  const EVENTS_FILE = "events.jsonl";
9
9
  const NODES_DIRECTORY = "nodes";
10
- function runPaths(workspaceRoot, runId) {
10
+ const runPaths = (workspaceRoot, runId) => {
11
11
  const runsRoot = join(workspaceRoot, RUNS_DIRECTORY);
12
12
  const runRoot = join(runsRoot, runId);
13
13
  return {
@@ -18,6 +18,6 @@ function runPaths(workspaceRoot, runId) {
18
18
  runsRoot,
19
19
  status: join(runRoot, STATUS_FILE)
20
20
  };
21
- }
21
+ };
22
22
  //#endregion
23
23
  export { RUNS_DIRECTORY, runPaths };
@@ -4,6 +4,6 @@ import "./store-manifest.js";
4
4
  import "./store-fs-effects.js";
5
5
  import "./store-paths.js";
6
6
  import "./workspace.js";
7
- import "effect";
8
7
  import "node:path";
8
+ import "effect";
9
9
  export {};
@@ -2,11 +2,11 @@ import { loadMokaDbUrl } from "../moka-global-config.js";
2
2
  import { recordNodeResult } from "../runtime/step/step-node.js";
3
3
  import { resolveDurableStore } from "../runtime/durable-store/acquisition.js";
4
4
  import { parseSubmitResult } from "../runtime/node-protocol/node-protocol.js";
5
- import { Effect } from "effect";
5
+ import * as Effect from "effect/Effect";
6
6
  //#region src/run-control/submit-result.ts
7
7
  /**
8
8
  * PIPE-91.7: validate and persist a node's terminal result into the durable
9
- * store. Throws a {@link z.ZodError} on malformed payload or a
9
+ * store. Throws an Effect Schema validation error on malformed payload or a
10
10
  * `result.nodeId !== nodeId` mismatch; throws a {@link SyntaxError} on
11
11
  * invalid JSON.
12
12
  *
@@ -16,7 +16,7 @@ import { Effect } from "effect";
16
16
  * gate evaluation must look up the schedule artifact, not this store entry.
17
17
  * `inputs` are opaque and `undefined` until a future lane pins the schema.
18
18
  */
19
- function recordSubmitResult(input) {
19
+ const recordSubmitResult = (input) => {
20
20
  const raw = JSON.parse(input.resultJson);
21
21
  const parsed = parseSubmitResult({
22
22
  nodeId: input.nodeId,
@@ -28,7 +28,22 @@ function recordSubmitResult(input) {
28
28
  runId: parsed.runId,
29
29
  store: input.store
30
30
  });
31
- }
31
+ };
32
+ const submitResultEffect = (runId, nodeId, resultJson) => Effect.scoped(Effect.gen(function* effectBody() {
33
+ const store = yield* resolveDurableStore(loadMokaDbUrl(), runId);
34
+ yield* Effect.try({
35
+ catch: (error) => error,
36
+ try: () => {
37
+ recordSubmitResult({
38
+ nodeId,
39
+ resultJson,
40
+ runId,
41
+ store
42
+ });
43
+ }
44
+ });
45
+ process.stdout.write(`Recorded result for run ${runId} node ${nodeId}.\n`);
46
+ }));
32
47
  /**
33
48
  * PIPE-91.7: register `moka submit-result <run-id> <node-id> --json <payload>`.
34
49
  * A distinct top-level command (NOT a `submit` group — `moka submit` already
@@ -36,25 +51,10 @@ function recordSubmitResult(input) {
36
51
  * string; `(runId, nodeId)` are the positional routing keys — mirroring
37
52
  * `moka next node <run-id>` (PIPE-91.6).
38
53
  */
39
- function registerSubmitResultSubcommand(program) {
54
+ const registerSubmitResultSubcommand = (program) => {
40
55
  program.command("submit-result").description("Persist a node's terminal result into the durable run store").argument("<run-id>", "the run id to persist the result under").argument("<node-id>", "the node id whose result is being submitted").requiredOption("--json <payload>", "the RuntimeNodeResult as a JSON string").action(async (runId, nodeId, flags) => {
41
56
  await Effect.runPromise(submitResultEffect(runId, nodeId, flags.json));
42
57
  });
43
- }
44
- function submitResultEffect(runId, nodeId, resultJson) {
45
- return Effect.scoped(Effect.gen(function* () {
46
- const store = yield* resolveDurableStore(loadMokaDbUrl(), runId);
47
- yield* Effect.try({
48
- catch: (error) => error,
49
- try: () => recordSubmitResult({
50
- nodeId,
51
- resultJson,
52
- runId,
53
- store
54
- })
55
- });
56
- process.stdout.write(`Recorded result for run ${runId} node ${nodeId}.\n`);
57
- }));
58
- }
58
+ };
59
59
  //#endregion
60
60
  export { recordSubmitResult, registerSubmitResultSubcommand };