@oisincoveney/pipeline 3.24.3 → 4.0.1

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 (171) hide show
  1. package/README.md +12 -0
  2. package/dist/argo-graph.js +4 -2
  3. package/dist/argo-submit.d.ts +1 -1
  4. package/dist/argo-submit.js +39 -31
  5. package/dist/argo-workflow.d.ts +0 -1
  6. package/dist/cli/doctor.js +5 -3
  7. package/dist/cli/factory-commands.js +1 -2
  8. package/dist/cli/program.d.ts +1 -1
  9. package/dist/cli/program.js +8 -2
  10. package/dist/cli/run-command.d.ts +1 -1
  11. package/dist/cli/run-commands.js +7 -5
  12. package/dist/cli/run-service.d.ts +0 -1
  13. package/dist/cli/run-service.js +11 -8
  14. package/dist/cli/submit-options.js +10 -4
  15. package/dist/commands/coordinator-command.js +35 -0
  16. package/dist/commands/pipeline-command.js +4 -2
  17. package/dist/commands/runner-command-command.js +11 -6
  18. package/dist/commands/runner-node-command.js +31 -0
  19. package/dist/commands/ticket/create.js +4 -2
  20. package/dist/commands/ticket/sequence.js +2 -1
  21. package/dist/commands/ticket/shared.d.ts +4 -1
  22. package/dist/config/defaults.d.ts +0 -1
  23. package/dist/config/load.d.ts +0 -1
  24. package/dist/config/load.js +3 -1
  25. package/dist/config/schema/catalog.d.ts +2 -1
  26. package/dist/config/schema/catalog.js +2 -1
  27. package/dist/config/schemas.d.ts +7 -3
  28. package/dist/config/schemas.js +3 -1
  29. package/dist/config/validate.d.ts +0 -1
  30. package/dist/config.d.ts +2 -2
  31. package/dist/context/repo-map.js +5 -2
  32. package/dist/coordinator/composition.js +169 -0
  33. package/dist/coordinator/config.js +71 -0
  34. package/dist/coordinator/run-owner.js +64 -0
  35. package/dist/coordinator/wave-advance.js +148 -0
  36. package/dist/coordinator/wave-scheduler.js +50 -0
  37. package/dist/factory/create-experiment.js +252 -84
  38. package/dist/hooks.d.ts +0 -1
  39. package/dist/index.d.ts +3 -2
  40. package/dist/index.js +6 -1
  41. package/dist/install-commands/claude-code.js +4 -0
  42. package/dist/install-commands/opencode.js +2 -1
  43. package/dist/install-commands/planner.js +6 -3
  44. package/dist/loop/argo-poll.d.ts +0 -1
  45. package/dist/loop/argo-poll.js +3 -64
  46. package/dist/loop/controller-deps.js +9 -7
  47. package/dist/loop/controller.js +5 -3
  48. package/dist/loop/loop-controller-entrypoint.js +2 -1
  49. package/dist/loop/run-terminal-poll.js +69 -0
  50. package/dist/mcp/gateway-doctor.js +1 -31
  51. package/dist/mcp/repo-local-backends.js +2 -1
  52. package/dist/moka-global-config.d.ts +0 -1
  53. package/dist/moka-global-config.js +6 -3
  54. package/dist/moka-submit.d.ts +17 -3
  55. package/dist/pipeline-runtime.d.ts +5 -4
  56. package/dist/pipeline-runtime.js +4 -2
  57. package/dist/planning/compile.d.ts +2 -2
  58. package/dist/planning/generate.d.ts +3 -3
  59. package/dist/planning/generate.js +5 -3
  60. package/dist/planning/graph.d.ts +0 -1
  61. package/dist/remote/argo/model.d.ts +1 -1
  62. package/dist/remote/jobs/execution-identity.js +70 -0
  63. package/dist/remote/jobs/job-outcome.js +108 -0
  64. package/dist/remote/jobs/manifest.js +96 -0
  65. package/dist/remote/jobs/model.d.ts +158 -0
  66. package/dist/remote/jobs/model.js +162 -0
  67. package/dist/remote/jobs/node-bootstrap.js +213 -0
  68. package/dist/remote/jobs/policy.js +123 -0
  69. package/dist/remote/jobs/reconcile.js +100 -0
  70. package/dist/remote/jobs/run-spec-client.js +64 -0
  71. package/dist/remote/jobs/run-spec-path.js +31 -0
  72. package/dist/remote/jobs/run-spec-server.d.ts +34 -0
  73. package/dist/remote/jobs/run-spec-server.js +116 -0
  74. package/dist/remote/jobs/submit.d.ts +23 -0
  75. package/dist/remote/jobs/submit.js +116 -0
  76. package/dist/remote/jobs/watch.js +59 -0
  77. package/dist/remote/submit/argo-submission.d.ts +3 -1
  78. package/dist/remote/submit/compilation.d.ts +2 -0
  79. package/dist/remote/submit/hook-events.d.ts +1 -0
  80. package/dist/remote/submit/io.d.ts +7 -1
  81. package/dist/remote/submit/jobs-submission.d.ts +59 -0
  82. package/dist/remote/submit/jobs-submission.js +97 -0
  83. package/dist/remote/submit/jobs-submit-result.d.ts +25 -0
  84. package/dist/remote/submit/jobs-submit-result.js +34 -0
  85. package/dist/remote/submit/options.d.ts +0 -1
  86. package/dist/remote/submit/service.d.ts +40 -1
  87. package/dist/remote/submit/service.js +23 -1
  88. package/dist/run-control/contracts.d.ts +12 -1
  89. package/dist/run-control/contracts.js +28 -1
  90. package/dist/run-control/execution-store.js +0 -0
  91. package/dist/run-control/next-node.js +2 -1
  92. package/dist/run-control/postgres/postgres-run-control-store.js +4 -4
  93. package/dist/run-control/resume-command.js +2 -1
  94. package/dist/run-control/run-artifacts-command.js +2 -1
  95. package/dist/run-control/run-record.js +3 -1
  96. package/dist/run-control/runtime-reporter.js +3 -1
  97. package/dist/run-control/supervisor.js +2 -1
  98. package/dist/run-state/git-refs.js +2 -1
  99. package/dist/runner-command/environment-commands.js +91 -0
  100. package/dist/runner-command/lifecycle-context.js +5 -2
  101. package/dist/runner-command/lifecycle.js +1 -1
  102. package/dist/runner-command/pre-schedule.js +3 -2
  103. package/dist/runner-command/run.js +65 -48
  104. package/dist/runner-command/select-ready-wave.js +1 -1
  105. package/dist/runner-command-contract.d.ts +5 -6
  106. package/dist/runner-command-contract.js +11 -10
  107. package/dist/runner-event-schema.d.ts +12 -13
  108. package/dist/runner.d.ts +2 -2
  109. package/dist/runner.js +6 -5
  110. package/dist/runtime/actor-ids.d.ts +1 -0
  111. package/dist/runtime/agent-node/agent-node.js +3 -2
  112. package/dist/runtime/agent-node/output-finalization.js +3 -2
  113. package/dist/runtime/agent-node/prompt-rendering.js +2 -1
  114. package/dist/runtime/agent-node/session-execution.js +17 -9
  115. package/dist/runtime/builtins/builtins.js +10 -4
  116. package/dist/runtime/changed-files/changed-files.js +5 -2
  117. package/dist/runtime/contracts/contracts.d.ts +3 -0
  118. package/dist/runtime/drain-merge/drain-merge.js +2 -1
  119. package/dist/runtime/durable-store/durable-store.d.ts +2 -0
  120. package/dist/runtime/durable-store/postgres/client.js +29 -0
  121. package/dist/runtime/durable-store/postgres/migrate-substrate.js +6 -4
  122. package/dist/runtime/durable-store/postgres/postgres-store.js +2 -3
  123. package/dist/runtime/events/events.js +27 -17
  124. package/dist/runtime/events/index.js +2 -2
  125. package/dist/runtime/execution-envelope.js +44 -0
  126. package/dist/runtime/gates/kinds/acceptance/acceptance.js +2 -1
  127. package/dist/runtime/handoff.d.ts +2 -1
  128. package/dist/runtime/hooks/module-hook.js +2 -1
  129. package/dist/runtime/journal-acquisition.d.ts +2 -1
  130. package/dist/runtime/json-validation/json-validation.js +2 -1
  131. package/dist/runtime/launch-plan-args.js +39 -0
  132. package/dist/runtime/node-execution.js +5 -3
  133. package/dist/runtime/node-state-store.d.ts +3 -0
  134. package/dist/runtime/open-pull-request/open-pull-request.js +76 -18
  135. package/dist/runtime/opencode-runtime.js +32 -10
  136. package/dist/runtime/opencode-session-executor.js +8 -21
  137. package/dist/runtime/parallel-node/parallel-node.js +2 -1
  138. package/dist/runtime/run-journal.d.ts +2 -0
  139. package/dist/runtime/scheduled-dependencies.d.ts +1 -0
  140. package/dist/runtime/scheduled-dependencies.js +2 -1
  141. package/dist/runtime/scheduler.js +5 -3
  142. package/dist/runtime/services/backlog-service.d.ts +1 -2
  143. package/dist/runtime/services/kubernetes-argo-service.d.ts +0 -1
  144. package/dist/runtime/services/kubernetes-jobs-read-service.js +115 -0
  145. package/dist/runtime/services/kubernetes-jobs-service.d.ts +37 -0
  146. package/dist/runtime/services/kubernetes-jobs-service.js +45 -0
  147. package/dist/runtime/services/kubernetes-jobs-watch-service.js +61 -0
  148. package/dist/runtime/services/kubernetes-lease-service.js +21 -0
  149. package/dist/runtime/services/mcp-gateway-service.js +63 -15
  150. package/dist/runtime/services/runner-event-sink-http-service.js +2 -1
  151. package/dist/runtime/workflow-execution.js +2 -1
  152. package/dist/runtime/workflow-lifecycle.js +2 -1
  153. package/dist/runtime/yeet-event-record.js +104 -0
  154. package/dist/runtime/yeet-executor.js +184 -0
  155. package/dist/schedule/backlog-context.js +4 -2
  156. package/dist/schedule/passes/open-pull-request.js +4 -2
  157. package/dist/schedule/prompts.js +2 -1
  158. package/dist/schema-boundary.d.ts +4 -1
  159. package/dist/tickets/apply-ticket-plan.js +2 -1
  160. package/dist/tickets/backlog-task-store.d.ts +2 -1
  161. package/dist/tickets/backlog-task-store.js +4 -2
  162. package/dist/tickets/ticket-graph-dto.d.ts +6 -7
  163. package/dist/tickets/ticket-graph-dto.js +5 -5
  164. package/dist/tickets/ticket-graph.d.ts +1 -2
  165. package/dist/tickets/ticket-plan.d.ts +1 -1
  166. package/dist/tickets/ticket-selection.d.ts +2 -0
  167. package/dist/workflow-submit-contract.d.ts +0 -1
  168. package/docs/adr-moka-postgres-schema-isolation.md +48 -0
  169. package/docs/pipeline-console-runner-contract.md +10 -6
  170. package/docs/runtime-test-subsystems.md +200 -0
  171. package/package.json +3 -36
package/README.md CHANGED
@@ -379,6 +379,18 @@ Package and container publishing is owned by GitHub Actions. Do not publish from
379
379
  a workstation with `npm publish`, `semantic-release`, Docker pushes, or direct
380
380
  registry commands.
381
381
 
382
+ `@oisincoveney/pipeline` is published by `engine`'s own root-level
383
+ `.github/workflows/publish-pipeline.yml`, from this directory's current
384
+ (contract-v2) source, on every push to `main` under `runner/**`. The legacy
385
+ `oisin-ee/pipeline-runner` repository's publish workflow is retired — it
386
+ predates the ENG-67.3 contract-v2 bump and must not be used to cut a release.
387
+ The workflow authenticates to `registry.npmjs.org` via npm Trusted Publishing
388
+ (OIDC) rather than a static `NPM_TOKEN`; no npm credential is stored as a
389
+ GitHub secret in this org. Trusted Publishing must be configured on
390
+ npmjs.com for `@oisincoveney/pipeline`, bound to `oisin-ee/engine` and the
391
+ `publish-pipeline.yml` workflow filename, before this workflow can publish. A
392
+ successful release also triggers the runner container image build.
393
+
382
394
  The authoritative package version lives in the npm registry, not in this
383
395
  repository. `package.json` pins the `0.0.0-development` semantic-release
384
396
  sentinel and is never bumped in git; semantic-release derives and publishes the
@@ -75,8 +75,9 @@ const assertUniqueNodeIds = (nodes) => {
75
75
  };
76
76
  const resolveDependencyTaskNames = (context, nodeIds) => uniqueStrings(resolveExecutableDependencyIds(context.nodeById, nodeIds).map((id) => argoTaskName(id)));
77
77
  const compileExecutableNode = (context, node, inheritedNeeds) => {
78
+ const dependencies = resolveDependencyTaskNames(context, [...inheritedNeeds, ...node.needs]);
78
79
  return parseStrictWithSchema(argoExecutableTaskSchema, {
79
- dependencies: resolveDependencyTaskNames(context, [...inheritedNeeds, ...node.needs]),
80
+ dependencies,
80
81
  nodeId: node.id,
81
82
  taskName: argoTaskName(node.id),
82
83
  templateName: argoTemplateName(node.id)
@@ -90,7 +91,8 @@ const compileArgoNodes = (context, nodes, inheritedNeeds, initialTasks = []) =>
90
91
  }));
91
92
  const compileArgoNode = (input) => Match.value(input.node.kind).pipe(Match.whenOr("agent", "builtin", "command", () => [...input.tasks, compileExecutableNode(input.context, input.node, input.inheritedNeeds)]), Match.when("group", () => input.tasks), Match.when("parallel", () => compileArgoNodes(input.context, input.node.children ?? [], [...input.inheritedNeeds, ...input.node.needs], input.tasks)), Match.exhaustive);
92
93
  const compileArgoExecutionGraphUnchecked = (plan) => {
93
- const tasks = compileArgoNodes({ nodeById: indexPlannedNodesById(plan.topologicalOrder) }, plan.topologicalOrder, []);
94
+ const context = { nodeById: indexPlannedNodesById(plan.topologicalOrder) };
95
+ const tasks = compileArgoNodes(context, plan.topologicalOrder, []);
94
96
  const terminalTasks = terminalDependencyItems(tasks, (task) => task.taskName, (task) => task.dependencies);
95
97
  return {
96
98
  tasks,
@@ -1,8 +1,8 @@
1
1
  import { PipelineConfig } from "./config/schemas.js";
2
+ import "./config.js";
2
3
  import { CoreApi, KubernetesArgoIoDependencies, WorkflowApi } from "./runtime/services/kubernetes-argo-service.js";
3
4
  import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
4
5
  import * as Schema from "effect/Schema";
5
-
6
6
  //#region src/argo-submit.d.ts
7
7
  declare const commandScheduleOptionsSchema: Schema.Struct<{
8
8
  readonly command: Schema.mutable<Schema.$Array<Schema.NonEmptyString>>;
@@ -169,8 +169,9 @@ const ownRunConfigMaps = (input) => {
169
169
  error: new ArgoSubmitError({ message: missingWorkflowUidMessage(input.result) }),
170
170
  result: input.result
171
171
  }).pipe(Effect.as(input.result));
172
+ const body = configMapOwnerReferencesPatch(ownerReference.value);
172
173
  return patchRunConfigMapOwnerReferences({
173
- body: configMapOwnerReferencesPatch(ownerReference.value),
174
+ body,
174
175
  configMapNames: input.configMapNames,
175
176
  dependencies: input.dependencies,
176
177
  namespace: input.namespace,
@@ -229,12 +230,13 @@ const workflowSubmitResult = (response, workflow, base) => {
229
230
  const submitWorkflowManifest = (input) => Effect.gen(function* effectBody() {
230
231
  const service = yield* KubernetesArgoService;
231
232
  const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
232
- return workflowSubmitResult(yield* service.createWorkflow({
233
+ const response = yield* service.createWorkflow({
233
234
  body: parseStrictWithSchema(runnerArgoWorkflowManifestSchema, workflow),
234
235
  dependencies: input.dependencies,
235
236
  namespace: input.namespace,
236
237
  options: input.options
237
- }), workflow, {
238
+ });
239
+ return workflowSubmitResult(response, workflow, {
238
240
  namespace: input.namespace,
239
241
  ...input.resultExtras
240
242
  });
@@ -294,8 +296,9 @@ const normalizeRunnerPayloadForSubmit = (input) => {
294
296
  const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
295
297
  const { config: _config, ...schemaOptions } = rawOptions;
296
298
  const options = parseStrictWithSchema(submitDynamicRunnerArgoWorkflowOptionsSchema, schemaOptions);
299
+ const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
297
300
  const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
298
- payload: parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson)),
301
+ payload: parsedPayload,
299
302
  payloadJson: options.payloadJson
300
303
  });
301
304
  if (payload.workflow.id !== options.workflowId) return Effect.fail(new ArgoSubmitError({ message: `Runner payload workflow '${payload.workflow.id}' does not match dynamic workflow '${options.workflowId}'` }));
@@ -335,18 +338,20 @@ const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
335
338
  workflowId: options.workflowId
336
339
  });
337
340
  return Effect.gen(function* effectBody() {
341
+ const configMaps = dynamicRunConfigMaps({
342
+ labels,
343
+ namespace: options.namespace,
344
+ payloadConfigMapName,
345
+ payloadJson
346
+ });
347
+ const createdConfigMapNames = yield* createRunConfigMaps({
348
+ configMaps,
349
+ dependencies,
350
+ namespace: options.namespace,
351
+ options
352
+ });
338
353
  return yield* submitWorkflowWithRunConfigMaps({
339
- configMapNames: yield* createRunConfigMaps({
340
- configMaps: dynamicRunConfigMaps({
341
- labels,
342
- namespace: options.namespace,
343
- payloadConfigMapName,
344
- payloadJson
345
- }),
346
- dependencies,
347
- namespace: options.namespace,
348
- options
349
- }),
354
+ configMapNames: createdConfigMapNames,
350
355
  dependencies,
351
356
  namespace: options.namespace,
352
357
  options,
@@ -367,8 +372,9 @@ const compileSubmitArgoGraph = (compiled) => Effect.try({
367
372
  const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
368
373
  const { config, ...schemaOptions } = rawOptions;
369
374
  const options = parseStrictWithSchema(submitRunnerArgoWorkflowOptionsSchema, schemaOptions);
375
+ const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
370
376
  const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
371
- payload: parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson)),
377
+ payload: parsedPayload,
372
378
  payloadJson: options.payloadJson
373
379
  });
374
380
  const compiled = compileScheduleArtifact(config, parseScheduleArtifact(options.scheduleYaml, "schedule.yaml"));
@@ -415,22 +421,24 @@ const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
415
421
  });
416
422
  return Effect.gen(function* effectBody() {
417
423
  const graph = yield* graphEffect;
424
+ const configMaps = staticRunConfigMaps({
425
+ labels,
426
+ namespace: options.namespace,
427
+ payloadConfigMapName,
428
+ payloadJson,
429
+ scheduleConfigMapName: scheduleArtifactConfigMapName,
430
+ scheduleYaml: options.scheduleYaml,
431
+ taskDescriptorConfigMapName,
432
+ tasks: graph.tasks
433
+ });
434
+ const createdConfigMapNames = yield* createRunConfigMaps({
435
+ configMaps,
436
+ dependencies,
437
+ namespace: options.namespace,
438
+ options
439
+ });
418
440
  return yield* submitWorkflowWithRunConfigMaps({
419
- configMapNames: yield* createRunConfigMaps({
420
- configMaps: staticRunConfigMaps({
421
- labels,
422
- namespace: options.namespace,
423
- payloadConfigMapName,
424
- payloadJson,
425
- scheduleConfigMapName: scheduleArtifactConfigMapName,
426
- scheduleYaml: options.scheduleYaml,
427
- taskDescriptorConfigMapName,
428
- tasks: graph.tasks
429
- }),
430
- dependencies,
431
- namespace: options.namespace,
432
- options
433
- }),
441
+ configMapNames: createdConfigMapNames,
434
442
  dependencies,
435
443
  namespace: options.namespace,
436
444
  options,
@@ -1,6 +1,5 @@
1
1
  import { WorkflowExecutionPlan } from "./planning/compile.js";
2
2
  import { buildDynamicRunnerArgoWorkflowOptionsSchema, buildRunnerArgoWorkflowOptionsSchema } from "./remote/argo/model.js";
3
-
4
3
  //#region src/argo-workflow.d.ts
5
4
  declare const runnerArgoWorkflowManifestSchema: import("effect/Schema").Struct<{
6
5
  readonly apiVersion: import("effect/Schema").Literal<"argoproj.io/v1alpha1">;
@@ -188,10 +188,11 @@ const checkMokaAgents = async (cwd, config) => {
188
188
  passed: true
189
189
  } };
190
190
  try {
191
- const visible = visibleAgentNames((await execa("opencode", OPENCODE_AGENT_LIST_ARGS, {
191
+ const result = await execa("opencode", OPENCODE_AGENT_LIST_ARGS, {
192
192
  cwd,
193
193
  stdin: "ignore"
194
- })).stdout);
194
+ });
195
+ const visible = visibleAgentNames(result.stdout);
195
196
  if (!visible.recognized) return skippedAgentVisibility("OpenCode agent listing output was not recognized");
196
197
  if (visible.ambiguous && expected.every((name) => !visible.names.has(name))) return skippedAgentVisibility("OpenCode agent listing output did not include recognizable MoKa agent names");
197
198
  const missing = expected.filter((name) => !visible.names.has(name));
@@ -211,7 +212,8 @@ const checkMokaAgents = async (cwd, config) => {
211
212
  const headlessPermissionWarning = (path, entry) => {
212
213
  try {
213
214
  if (!statSync(path).isFile()) return [];
214
- const risky = interactivePermissionPaths(matter(readFileSync(path, "utf-8")).data.permission);
215
+ const parsed = matter(readFileSync(path, "utf-8"));
216
+ const risky = interactivePermissionPaths(parsed.data.permission);
215
217
  if (risky.length === 0) return [];
216
218
  return [{
217
219
  detail: `${entry} requires interactive permission prompts at ${risky.join(", ")}; headless MoKa runs may block.`,
@@ -4,14 +4,13 @@ import { Option } from "commander";
4
4
  //#region src/cli/factory-commands.ts
5
5
  const hasText = (value) => typeof value === "string" && value !== "";
6
6
  const registerFactoryCommands = (program) => {
7
- program.command("create-experiment").description("Birth a fleet experiment: copier-stamp momokaya-template, create+push the org repo, register it in infra's fleet registry").requiredOption("--name <name>", "app name (kebab-case)").addOption(new Option("--flavor <flavor>", "app flavor").choices(["web", "expo-web"]).default("web")).option("--no-db", "skip the database surface").option("--no-previews", "skip per-PR preview environments").option("--org <org>", "GitHub org for the new repo").option("--template-src <source>", "copier template source").option("--template-ref <ref>", "template tag/ref (default: latest tag)").option("--infra-repo-url <url>", "infra repo the registry entry lands in").action(async (flags) => {
7
+ program.command("create-experiment").description("Birth a fleet experiment: copier-stamp momokaya-template, create+push the org repo, register it in infra's fleet registry").requiredOption("--name <name>", "app name (kebab-case)").addOption(new Option("--flavor <flavor>", "app flavor").choices(["web", "expo-web"]).default("web")).option("--no-db", "skip the database surface").option("--org <org>", "GitHub org for the new repo").option("--template-src <source>", "copier template source").option("--template-ref <ref>", "template tag/ref (default: latest tag)").option("--infra-repo-url <url>", "infra repo the registry entry lands in").action(async (flags) => {
8
8
  const result = await runCreateExperiment({
9
9
  db: flags.db,
10
10
  flavor: flags.flavor,
11
11
  ...hasText(flags.infraRepoUrl) ? { infraRepoUrl: flags.infraRepoUrl } : {},
12
12
  name: flags.name,
13
13
  ...hasText(flags.org) ? { org: flags.org } : {},
14
- previews: flags.previews,
15
14
  ...hasText(flags.templateRef) ? { templateRef: flags.templateRef } : {},
16
15
  ...hasText(flags.templateSrc) ? { templateSource: flags.templateSrc } : {}
17
16
  });
@@ -1,8 +1,8 @@
1
1
  import { RunCommand } from "./run-command.js";
2
2
  import { TicketCommandOptions } from "../commands/ticket/shared.js";
3
+ import "../commands/ticket-command.js";
3
4
  import { Effect } from "effect";
4
5
  import { Command } from "commander";
5
-
6
6
  //#region src/cli/program.d.ts
7
7
  interface CliProgramOptions {
8
8
  readonly runCommand?: RunCommand;
@@ -1,8 +1,10 @@
1
1
  import { loadPipelineConfig } from "../config/load.js";
2
2
  import "../config.js";
3
3
  import { registerBenchCommand } from "../commands/bench-command.js";
4
+ import { registerCoordinatorCommand } from "../commands/coordinator-command.js";
4
5
  import { registerConfiguredEntrypointCommands } from "../commands/pipeline-command.js";
5
6
  import { registerRunnerCommandCommand } from "../commands/runner-command-command.js";
7
+ import { registerRunnerNodeCommand } from "../commands/runner-node-command.js";
6
8
  import { registerTicketCommand } from "../commands/ticket-command.js";
7
9
  import { addMokaSubmitOptions, runMokaSubmitFromCli } from "./submit-options.js";
8
10
  import { registerRunControlCommands } from "../run-control/commands.js";
@@ -32,6 +34,8 @@ const registerApplicationCommands = (program, options) => {
32
34
  registerLoopCommand(program);
33
35
  registerFactoryCommands(program);
34
36
  registerRunnerCommandCommand(program);
37
+ registerRunnerNodeCommand(program);
38
+ registerCoordinatorCommand(program);
35
39
  registerBenchCommand(program);
36
40
  registerTicketCommand(program, {
37
41
  ...options.ticketCommand,
@@ -49,7 +53,8 @@ const runEntrypointCommand = async (entrypoint, task) => {
49
53
  await execute(task, { entrypoint });
50
54
  };
51
55
  const registerEntrypointCommands = (program, configuredPipeline) => {
52
- configureEntrypointHelp(program, registerConfiguredEntrypointCommands(program, configuredPipeline, runEntrypointCommand));
56
+ const configuredEntrypointCommands = registerConfiguredEntrypointCommands(program, configuredPipeline, runEntrypointCommand);
57
+ configureEntrypointHelp(program, configuredEntrypointCommands);
53
58
  };
54
59
  const isPackageVersionRecord = (value) => typeof value === "object" && value !== null && "version" in value && typeof value.version === "string";
55
60
  const readPackageVersion = () => {
@@ -60,7 +65,8 @@ const readPackageVersion = () => {
60
65
  const createBaseProgram = () => new Command().name("moka").description("Submit work to Momokaya").version(readPackageVersion()).exitOverride();
61
66
  const loadConfiguredEntrypoints = (cwd) => loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
62
67
  const createCliProgram = (options = {}) => {
63
- const configuredPipeline = loadConfiguredEntrypoints(process.env.PIPELINE_TARGET_PATH ?? process.cwd());
68
+ const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
69
+ const configuredPipeline = loadConfiguredEntrypoints(cwd);
64
70
  const program = createBaseProgram();
65
71
  registerApplicationCommands(program, options);
66
72
  registerEntrypointCommands(program, configuredPipeline);
@@ -1,5 +1,5 @@
1
+ import "../run-control/contracts.js";
1
2
  import { RunResolution, RunResolverFlags } from "./run-resolver.js";
2
-
3
3
  //#region src/cli/run-command.d.ts
4
4
  interface RunCommandCall {
5
5
  readonly descriptionParts: string[];
@@ -23,7 +23,8 @@ const createResolvedRunCommand = (options) => async (call) => {
23
23
  await runLocalResolvedTask(resolvedTask, execution, runControl);
24
24
  },
25
25
  runRemoteSubmit: async ({ descriptionParts: parts, execution }) => {
26
- printMokaSubmitResult(await runMokaSubmitFromCli(parts, remoteSubmitFlags(execution)));
26
+ const result = await runMokaSubmitFromCli(parts, remoteSubmitFlags(execution));
27
+ printMokaSubmitResult(result);
27
28
  }
28
29
  });
29
30
  };
@@ -31,13 +32,14 @@ const registerRunCommands = (program, options = {}) => {
31
32
  const dispatchResolvedRunCommand = createResolvedRunCommand(options);
32
33
  program.command("run").description("Primary command: run a workflow from package-owned @oisincoveney/pipeline config").argument("<description...>", "task description").option("--command", "treat input after -- as explicit argv for remote submission").option("--entrypoint <entrypoint>", "entrypoint id from package config").option("--detach", "start a supervised controller process in the background").addOption(new Option("--effort <effort>", "run effort").choices([...MOKA_RUN_EFFORTS]).default("normal")).option("--read-only", "run the read-only inspect workflow").option("--schedule <schedule>", "approved schedule YAML to execute").addOption(new Option("--target <target>", "execution target").choices([...MOKA_RUN_TARGETS]).default("local")).option("--workflow <workflow>", "workflow id from package config").action(async (descriptionParts, flags) => {
33
34
  const task = descriptionParts.join(" ");
35
+ const resolution = resolveMokaRun({
36
+ flags,
37
+ task
38
+ });
34
39
  await dispatchResolvedRunCommand({
35
40
  descriptionParts,
36
41
  flags,
37
- resolution: resolveMokaRun({
38
- flags,
39
- task
40
- }),
42
+ resolution,
41
43
  task
42
44
  });
43
45
  });
@@ -1,6 +1,5 @@
1
1
  import { RunEffort, RunMode, RunTarget } from "../run-control/contracts.js";
2
2
  import { runPipelineFromConfig } from "../pipeline-runtime.js";
3
-
4
3
  //#region src/cli/run-service.d.ts
5
4
  interface ExecuteOptions {
6
5
  entrypoint?: string;
@@ -171,7 +171,8 @@ const runPipelineSafely = async (inputs, runner, runStoreReporter) => await runW
171
171
  });
172
172
  const runAndPrintPipelineWithStore = async (inputs, store) => {
173
173
  const runner = inputs.pipelineRunner ?? runPipelineFromConfig;
174
- const runStoreReporter = await createRunStoreReporter(inputs, createTerminalRuntimeReporter(), store);
174
+ const terminalReporter = createTerminalRuntimeReporter();
175
+ const runStoreReporter = await createRunStoreReporter(inputs, terminalReporter, store);
175
176
  printSupervisedFollowUp(inputs);
176
177
  const result = await runPipelineSafely(inputs, runner, runStoreReporter);
177
178
  console.log(formatRuntimeResult(result));
@@ -318,14 +319,16 @@ const prepareDetachedRun = async (input) => {
318
319
  const runDetachedResolvedTask = async (task, execution, runControl) => {
319
320
  const worktreePath = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
320
321
  const runId = generateRuntimeRunId();
322
+ const config = loadPipelineConfig(worktreePath, { allowMissingLintFileReferences: true });
323
+ const prepared = await prepareDetachedRun({
324
+ config,
325
+ execution,
326
+ runId,
327
+ task,
328
+ worktreePath
329
+ });
321
330
  await persistDetachedRunController({
322
- prepared: await prepareDetachedRun({
323
- config: loadPipelineConfig(worktreePath, { allowMissingLintFileReferences: true }),
324
- execution,
325
- runId,
326
- task,
327
- worktreePath
328
- }),
331
+ prepared,
329
332
  runControl,
330
333
  runId,
331
334
  task,
@@ -22,6 +22,10 @@ const resolveMokaBrokerAuth = (globalConfig) => {
22
22
  if (brokerAuth === void 0) throw new Error("momokaya.submit.brokerAuth is required for remote submit");
23
23
  return brokerAuth;
24
24
  };
25
+ const deliveryFromFlags = (flags) => ({
26
+ ...flags.preview === void 0 ? {} : { preview: flags.preview },
27
+ pullRequest: flags.openPr === true
28
+ });
25
29
  const submitMokaCommandInput = (input, flags, commonOptions) => {
26
30
  if (flags.quick === true || flags.schedule !== void 0 && flags.schedule !== "") throw new Error("--command cannot be combined with --quick or --schedule");
27
31
  if (input.length === 0) throw new Error("Command argv is required when --command is set");
@@ -52,7 +56,7 @@ const addRunnerArgoOptions = (command, options = {}) => {
52
56
  "Never"
53
57
  ]).default("Always")).option("--image-pull-secret <name>", "imagePullSecret name");
54
58
  };
55
- const addMokaSubmitOptions = (command) => addRunnerArgoOptions(command.option("--quick", "submit the compact graph").option("--command", "treat input after -- as explicit argv").option("--schedule <path>", "approved schedule YAML to submit").option("--event-url <url>", "runner event sink URL").option("--open-pr", "append an open-pull-request delivery node (preview-labelled PR)").option("--task <text>", "task description for command-mode metadata").option("--db-auth-secret-name <name>", "override momokaya.submit.dbAuth secret name").option("--db-auth-secret-key <key>", "override momokaya.submit.dbAuth secret key").option("--skip-db-auth", "omit MOKA_DB_URL injection regardless of global config").option("--mcp-gateway-auth-secret-name <name>", "override momokaya.submit.mcpGatewayAuth secret name").option("--mcp-gateway-auth-secret-key <key>", "override momokaya.submit.mcpGatewayAuth secret key").option("--skip-mcp-gateway-auth", "omit PIPELINE_MCP_GATEWAY_AUTHORIZATION injection regardless of global config").option("--npm-registry-auth-secret-name <name>", "override momokaya.submit.npmRegistryAuthSecretName").option("--skip-npm-registry-auth", "omit the /root/.npmrc mount regardless of global config"), { kubeconfig: true });
59
+ const addMokaSubmitOptions = (command) => addRunnerArgoOptions(command.option("--quick", "submit the compact graph").option("--command", "treat input after -- as explicit argv").option("--schedule <path>", "approved schedule YAML to submit").option("--event-url <url>", "runner event sink URL").option("--open-pr", "append an open-pull-request delivery node").option("--preview", "add the canonical preview label to pull-request delivery").option("--no-preview", "remove the canonical preview label from existing pull-request delivery").option("--task <text>", "task description for command-mode metadata").option("--db-auth-secret-name <name>", "override momokaya.submit.dbAuth secret name").option("--db-auth-secret-key <key>", "override momokaya.submit.dbAuth secret key").option("--skip-db-auth", "omit MOKA_DB_URL injection regardless of global config").option("--mcp-gateway-auth-secret-name <name>", "override momokaya.submit.mcpGatewayAuth secret name").option("--mcp-gateway-auth-secret-key <key>", "override momokaya.submit.mcpGatewayAuth secret key").option("--skip-mcp-gateway-auth", "omit PIPELINE_MCP_GATEWAY_AUTHORIZATION injection regardless of global config").option("--npm-registry-auth-secret-name <name>", "override momokaya.submit.npmRegistryAuthSecretName").option("--skip-npm-registry-auth", "omit the /root/.npmrc mount regardless of global config"), { kubeconfig: true });
56
60
  const parseImagePullPolicy = (value) => {
57
61
  if (value === "IfNotPresent" || value === "Never") return value;
58
62
  return "Always";
@@ -67,7 +71,7 @@ const mokaCommonSubmitOptions = (input) => {
67
71
  secretName: input.flags.dbAuthSecretName,
68
72
  skip: input.flags.skipDbAuth
69
73
  }, momokaya?.submit.dbAuth),
70
- delivery: { pullRequest: input.flags.openPr === true },
74
+ delivery: deliveryFromFlags(input.flags),
71
75
  eventAuthSecretKey: momokaya?.submit.eventAuthSecretKey,
72
76
  eventAuthSecretName: momokaya?.submit.eventAuthSecretName,
73
77
  eventUrl: input.eventUrl,
@@ -107,11 +111,13 @@ const buildMokaSubmitInputFromCli = (input) => {
107
111
  };
108
112
  const runMokaSubmitFromCli = async (input, flags) => {
109
113
  const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
114
+ const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
115
+ const globalConfig = loadMokaGlobalConfig() ?? void 0;
110
116
  return await submitMoka(buildMokaSubmitInputFromCli({
111
- config: loadPipelineConfig(cwd, { allowMissingLintFileReferences: true }),
117
+ config,
112
118
  cwd,
113
119
  flags,
114
- globalConfig: loadMokaGlobalConfig() ?? void 0,
120
+ globalConfig,
115
121
  input
116
122
  }));
117
123
  };
@@ -0,0 +1,35 @@
1
+ import { createCoordinator } from "../coordinator/composition.js";
2
+ //#region src/commands/coordinator-command.ts
3
+ /**
4
+ * `coordinator` — the deployed coordinator process entrypoint (ENG-57.1,
5
+ * doc-1 decision 3). Starts the coordinator's RunSpec registry/HTTP server
6
+ * and default Jobs transport from the validated `~/.config/moka/config.yaml`,
7
+ * then blocks until SIGTERM/SIGINT (the chart Deployment's normal shutdown
8
+ * signal) so the RunSpec server's listening socket is released cleanly
9
+ * (AC#4) instead of the pod being killed out from under an open listener.
10
+ *
11
+ * Submission itself (`coordinator.submit`) is not triggered from this
12
+ * command — ENG-57.2/57.4 own wiring a caller (dynamic wave scheduling, run
13
+ * truth) onto the composed `Coordinator`. This command only owns the
14
+ * process lifecycle the chart Deployment needs to keep that composition
15
+ * alive and reachable.
16
+ */
17
+ const registerCoordinatorCommand = (program) => {
18
+ program.command("coordinator").description("Start the coordinator's RunSpec service and Jobs-transport composition (ENG-57.1)").action(async () => {
19
+ const coordinator = await createCoordinator();
20
+ const shutdown = async () => {
21
+ await coordinator.close();
22
+ process.exitCode = 0;
23
+ };
24
+ await new Promise((resolve) => {
25
+ process.once("SIGTERM", () => {
26
+ shutdown().then(resolve);
27
+ });
28
+ process.once("SIGINT", () => {
29
+ shutdown().then(resolve);
30
+ });
31
+ });
32
+ });
33
+ };
34
+ //#endregion
35
+ export { registerCoordinatorCommand };
@@ -30,7 +30,8 @@ const configureEntrypointOptions = (command, entrypoint) => {
30
30
  return command;
31
31
  };
32
32
  const createEntrypointCommand = (program, id, entrypoint) => {
33
- return configureEntrypointOptions(program.command(id).description(entrypoint.description ?? `Run the ${id} workflow`).argument("<description...>", "task description"), entrypoint);
33
+ const command = program.command(id).description(entrypoint.description ?? `Run the ${id} workflow`).argument("<description...>", "task description");
34
+ return configureEntrypointOptions(command, entrypoint);
34
35
  };
35
36
  const registerEntrypointAction = (command, id, serviceLive) => {
36
37
  command.action(async (descriptionParts, flags) => {
@@ -43,7 +44,8 @@ const registerConfiguredEntrypointCommands = (program, config, runEntrypoint) =>
43
44
  const reservedCommands = new Set(program.commands.map((command) => command.name()));
44
45
  for (const [id, entrypoint] of Object.entries(config.entrypoints)) {
45
46
  if (reservedCommands.has(id)) continue;
46
- registerEntrypointAction(createEntrypointCommand(program, id, entrypoint), id, serviceLive);
47
+ const command = createEntrypointCommand(program, id, entrypoint);
48
+ registerEntrypointAction(command, id, serviceLive);
47
49
  registered.add(id);
48
50
  reservedCommands.add(id);
49
51
  }
@@ -1,7 +1,7 @@
1
1
  import { runRunnerCommand } from "../runner-command/run.js";
2
+ import { runPreSchedulePhase } from "../runner-command/pre-schedule.js";
2
3
  import { runRunnerFinalize } from "../runner-command/finalize.js";
3
4
  import { runRunnerLifecycle } from "../runner-command/lifecycle.js";
4
- import { runPreSchedulePhase } from "../runner-command/pre-schedule.js";
5
5
  import { runSelectReadyWave } from "../runner-command/select-ready-wave.js";
6
6
  import { Context, Effect, Layer } from "effect";
7
7
  //#region src/commands/runner-command-command.ts
@@ -32,19 +32,24 @@ const setProcessExitCode = (exitCode) => Effect.sync(() => {
32
32
  process.exitCode = exitCode;
33
33
  });
34
34
  const runRunnerCommandEffect = (options) => Effect.gen(function* runRunnerCommandEffect() {
35
- yield* setProcessExitCode(yield* (yield* RunnerCommandService).run(options));
35
+ const exitCode = yield* (yield* RunnerCommandService).run(options);
36
+ yield* setProcessExitCode(exitCode);
36
37
  });
37
38
  const runRunnerLifecycleEffect = (options) => Effect.gen(function* runRunnerLifecycleEffect() {
38
- yield* setProcessExitCode(yield* (yield* RunnerCommandService).lifecycle(options));
39
+ const exitCode = yield* (yield* RunnerCommandService).lifecycle(options);
40
+ yield* setProcessExitCode(exitCode);
39
41
  });
40
42
  const runRunnerFinalizeEffect = (options) => Effect.gen(function* runRunnerFinalizeEffect() {
41
- yield* setProcessExitCode(yield* (yield* RunnerCommandService).finalize(options));
43
+ const exitCode = yield* (yield* RunnerCommandService).finalize(options);
44
+ yield* setProcessExitCode(exitCode);
42
45
  });
43
46
  const runPreScheduleEffect = (options) => Effect.gen(function* runPreScheduleEffect() {
44
- yield* setProcessExitCode(yield* (yield* RunnerCommandService).preSchedule(options));
47
+ const exitCode = yield* (yield* RunnerCommandService).preSchedule(options);
48
+ yield* setProcessExitCode(exitCode);
45
49
  });
46
50
  const runSelectReadyWaveEffect = (options) => Effect.gen(function* runSelectReadyWaveEffect() {
47
- yield* setProcessExitCode(yield* (yield* RunnerCommandService).selectReadyWave(options));
51
+ const exitCode = yield* (yield* RunnerCommandService).selectReadyWave(options);
52
+ yield* setProcessExitCode(exitCode);
48
53
  });
49
54
  const runRunnerProgram = async (program) => await Effect.runPromise(Effect.provide(program, RunnerCommandServiceLive));
50
55
  const registerRunnerCommandCommand = (program) => {
@@ -0,0 +1,31 @@
1
+ import { runJobNodeFromEnv } from "../remote/jobs/node-bootstrap.js";
2
+ //#region src/commands/runner-node-command.ts
3
+ /** Parse a commander string option into a non-negative integer, failing loudly on a bad value. */
4
+ const parseNonNegativeIntOption = (value, label) => {
5
+ if (value === void 0) return;
6
+ const parsed = Number(value);
7
+ if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`--${label} must be a non-negative integer, got '${value}'`);
8
+ return parsed;
9
+ };
10
+ /**
11
+ * `runner-node` — the Jobs transport container entrypoint (doc-1 decision 3,
12
+ * ENG-18). Fetches this node's RunSpec from the coordinator API (base URL +
13
+ * token supplied via the Job's env), runs it through the yeet executor, and
14
+ * exits with the agent's exit code so the Job's backoffLimit only retries a
15
+ * genuine crash (a non-zero process exit), never a semantic gate outcome.
16
+ */
17
+ const registerRunnerNodeCommand = (program) => {
18
+ program.command("runner-node").description("Fetch a node's RunSpec from the coordinator API and run it via yeet (Jobs transport)").option("--run-id <id>", "Run id (defaults to the PIPELINE_RUN_ID env)").option("--node-id <id>", "Node id (defaults to the PIPELINE_NODE_ID env)").option("--generation <n>", "Execution generation (defaults to the PIPELINE_EXECUTION_GENERATION env, or 0)").option("--attempt <n>", "Crash-retry attempt (defaults to the PIPELINE_EXECUTION_ATTEMPT env, or 1)").action(async (options) => {
19
+ const generation = parseNonNegativeIntOption(options.generation, "generation");
20
+ const attempt = parseNonNegativeIntOption(options.attempt, "attempt");
21
+ const result = await runJobNodeFromEnv({
22
+ ...options.nodeId === void 0 ? {} : { nodeId: options.nodeId },
23
+ ...options.runId === void 0 ? {} : { runId: options.runId },
24
+ ...generation === void 0 ? {} : { generation },
25
+ ...attempt === void 0 ? {} : { attempt }
26
+ }, process.env);
27
+ process.exitCode = result.agentResult.exitCode;
28
+ });
29
+ };
30
+ //#endregion
31
+ export { registerRunnerNodeCommand };
@@ -100,12 +100,14 @@ const runTicketScoperEffect = (launchPlan, executor) => Effect.gen(function* eff
100
100
  const formatAppliedTicketPlan = (applied) => ["Created tickets:", ...Object.entries(applied.taskIdsByKey).map(([key, taskId]) => ` ${key}: ${taskId}`)].join("\n");
101
101
  const printTicketCreateEffect = (worktreePath, request, flags, executor) => Effect.gen(function* effectBody() {
102
102
  yield* validateTicketCreateFlagsEffect(flags);
103
- const ticketPlan = yield* parseTicketPlanEffect(yield* runTicketScoperEffect(yield* ticketScoperLaunchPlanEffect(worktreePath, request), executor));
103
+ const launchPlan = yield* ticketScoperLaunchPlanEffect(worktreePath, request);
104
+ const ticketPlan = yield* parseTicketPlanEffect(yield* runTicketScoperEffect(launchPlan, executor));
104
105
  if (flags.dryRun === true) {
105
106
  yield* writeLineEffect(renderTicketPlanDryRun(ticketPlan));
106
107
  return;
107
108
  }
108
- yield* writeLineEffect(formatAppliedTicketPlan(yield* applyTicketPlanEffect(ticketPlan, worktreePath, { parentId: flags.parent })));
109
+ const applied = yield* applyTicketPlanEffect(ticketPlan, worktreePath, { parentId: flags.parent });
110
+ yield* writeLineEffect(formatAppliedTicketPlan(applied));
109
111
  });
110
112
  const registerCreateSubcommand = (ticketCommand, options) => {
111
113
  ticketCommand.command("create").description("Create a validated Backlog ticket plan").argument("<request...>", "ticket planning request").option("--dry-run", "render Backlog commands without writing tasks").option("--apply", "apply the validated ticket plan through Backlog").option("--parent <task-id>", "existing parent task for applied children").action(async (requestParts, flags) => {
@@ -5,7 +5,8 @@ import { Effect, Option } from "effect";
5
5
  const formatSequence = (batches) => batches.map((batch, index) => [`Sequence ${index + 1}:`, ...batch.map((id) => ` ${id}`)].join("\n")).join("\n\n");
6
6
  const printTicketSequenceEffect = (worktreePath, flags) => Effect.gen(function* effectBody() {
7
7
  const loaded = yield* loadTicketGraphEffect(worktreePath, Option.fromUndefinedOr(flags.root));
8
- yield* writeLineEffect(formatSequence(yield* sequenceTicketBatchesEffect(loaded.graph, loaded.scopedIds)));
8
+ const batches = yield* sequenceTicketBatchesEffect(loaded.graph, loaded.scopedIds);
9
+ yield* writeLineEffect(formatSequence(batches));
9
10
  });
10
11
  const registerSequenceSubcommand = (ticketCommand, _options) => {
11
12
  ticketCommand.command("sequence").description("Print dependency execution batches for Backlog tickets").option("--root <ticket-id>", "sequence one ticket tree").option("--plain", "print plain text output").action(async (flags) => {
@@ -1,8 +1,11 @@
1
1
  import { RunCommand } from "../../cli/run-command.js";
2
2
  import { AgentResult, RunnerExecutionOptions, RunnerLaunchPlan } from "../../runner.js";
3
3
  import { BacklogService } from "../../runtime/services/backlog-service.js";
4
+ import "../../runtime/services/repo-io-service.js";
5
+ import "../../tickets/backlog-task-store.js";
6
+ import "../../tickets/ticket-graph.js";
7
+ import "../../tickets/ticket-selection.js";
4
8
  import { Effect, Layer, Option } from "effect";
5
-
6
9
  //#region src/commands/ticket/shared.d.ts
7
10
  type TicketPlanExecutor = (plan: RunnerLaunchPlan, options: RunnerExecutionOptions) => Promise<AgentResult>;
8
11
  interface TicketCommandOptions {
@@ -1,5 +1,4 @@
1
1
  import * as Schema from "effect/Schema";
2
-
3
2
  //#region src/config/defaults.d.ts
4
3
  declare const PIPELINE_CONFIG_PATH = ".pipeline/pipeline.yaml";
5
4
  declare const RUNNERS_CONFIG_PATH = ".pipeline/runners.yaml";
@@ -1,5 +1,4 @@
1
1
  import { PipelineConfig, PipelineConfigParts, PipelineConfigValidationOptions } from "./schemas.js";
2
-
3
2
  //#region src/config/load.d.ts
4
3
  declare const loadPackagePipelineConfig: (projectRoot: string, options?: PipelineConfigValidationOptions) => PipelineConfig;
5
4
  declare const loadPipelineConfig: (projectRoot: string, options?: PipelineConfigValidationOptions) => PipelineConfig;
@@ -47,6 +47,7 @@ const assembledPipelineConfig = (pipeline, profiles, runners) => ({
47
47
  rules: profiles.rules,
48
48
  runner_command: pipeline.runner_command,
49
49
  runners: runners.runners,
50
+ runtime: pipeline.runtime,
50
51
  scheduler: pipeline.scheduler,
51
52
  schedules: pipeline.schedules,
52
53
  skills: profiles.skills,
@@ -58,7 +59,8 @@ const assembledPipelineConfig = (pipeline, profiles, runners) => ({
58
59
  const parsePipelineConfigPartsEffect = (sources, projectRoot = "", sourcePaths, options) => Effect.gen(function* effectBody() {
59
60
  const runners = yield* parseConfigYamlAs(sources.runners, sourcePaths.runners, runnersFileSchema);
60
61
  const profiles = yield* parseConfigYamlAs(sources.profiles, sourcePaths.profiles, profilesFileSchema);
61
- return validatePipelineConfig(assembledPipelineConfig(yield* parsePipelineFileEffect(sources.pipeline, sourcePaths.pipeline), profiles, runners), projectRoot, options);
62
+ const pipeline = yield* parsePipelineFileEffect(sources.pipeline, sourcePaths.pipeline);
63
+ return validatePipelineConfig(assembledPipelineConfig(pipeline, profiles, runners), projectRoot, options);
62
64
  });
63
65
  const loadPackagePipelineConfig = (projectRoot, options = {}) => {
64
66
  return runConfigIoSync(parsePipelineConfigPartsEffect({
@@ -1,5 +1,6 @@
1
1
  //#region src/config/schema/catalog.d.ts
2
2
  declare const RUNNER_TYPES: readonly ["opencode", "command"];
3
+ declare const RUNTIME_EXECUTORS: readonly ["sdk", "yeet"];
3
4
  declare const NODE_KINDS: readonly ["agent", "command", "builtin", "group", "parallel"];
4
5
  declare const HOOK_EVENTS: readonly ["workflow.start", "workflow.success", "workflow.failure", "workflow.complete", "node.start", "node.success", "node.error", "node.finish", "gate.failure"];
5
6
  declare const GATE_KINDS: readonly ["acceptance", "artifact", "builtin", "changed_files", "command", "json_schema", "verdict"];
@@ -8,4 +9,4 @@ declare const SCHEDULING_ROLES: readonly ["coverage", "implementation"];
8
9
  declare const MCP_GATEWAY_BACKEND_LOCALITIES: readonly ["repo-local", "repo-scoped-remote", "shared-remote"];
9
10
  declare const MCP_GATEWAY_WORKSPACE_PATH_SOURCES: readonly ["PIPELINE_TARGET_PATH", "cwd"];
10
11
  //#endregion
11
- export { GATE_KINDS, HOOK_EVENTS, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NODE_KINDS, RUNNER_TYPES, SCHEDULE_BASELINES, SCHEDULING_ROLES };
12
+ export { GATE_KINDS, HOOK_EVENTS, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NODE_KINDS, RUNNER_TYPES, RUNTIME_EXECUTORS, SCHEDULE_BASELINES, SCHEDULING_ROLES };