@oisincoveney/pipeline 3.18.0 → 3.19.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 (40) hide show
  1. package/README.md +21 -14
  2. package/defaults/pipeline.yaml +12 -12
  3. package/defaults/profiles.yaml +4 -4
  4. package/dist/argo-submit.d.ts +5 -1
  5. package/dist/argo-submit.js +89 -12
  6. package/dist/argo-workflow.d.ts +30 -4
  7. package/dist/argo-workflow.js +101 -4
  8. package/dist/commands/runner-command-command.js +20 -2
  9. package/dist/moka-submit.d.ts +2 -2
  10. package/dist/planning/generate.d.ts +21 -1
  11. package/dist/planning/generate.js +42 -2
  12. package/dist/remote/argo/model.d.ts +47 -2
  13. package/dist/remote/argo/model.js +46 -18
  14. package/dist/remote/argo/storage.js +34 -17
  15. package/dist/remote/argo/templates.js +103 -1
  16. package/dist/remote/submit/argo-submission.d.ts +2 -1
  17. package/dist/remote/submit/argo-submission.js +32 -8
  18. package/dist/remote/submit/compilation.d.ts +2 -1
  19. package/dist/remote/submit/compilation.js +11 -42
  20. package/dist/remote/submit/service.js +29 -6
  21. package/dist/run-control/commands.js +1 -1
  22. package/dist/run-control/next-node.js +29 -6
  23. package/dist/run-control/postgres/postgres-run-control-store.js +24 -5
  24. package/dist/run-control/run-artifacts-command.js +1 -1
  25. package/dist/run-control/run-query-command.js +1 -1
  26. package/dist/run-control/store-manifest.js +16 -3
  27. package/dist/run-control/store.js +1 -1
  28. package/dist/runner-command/dynamic-command.js +64 -0
  29. package/dist/runner-command/finalize.js +69 -6
  30. package/dist/runner-command/lifecycle-context.js +21 -4
  31. package/dist/runner-command/pre-schedule.js +195 -0
  32. package/dist/runner-command/run.js +29 -5
  33. package/dist/runner-command/schedule-source-options.js +15 -0
  34. package/dist/runner-command/select-ready-wave.js +35 -0
  35. package/dist/runner.js +1 -1
  36. package/dist/schedule/prompts.js +3 -0
  37. package/dist/tickets/ticket-plan.d.ts +44 -0
  38. package/dist/workflow-submit-contract.d.ts +2 -2
  39. package/dist/workflow-submit-contract.js +2 -2
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # @oisincoveney/pipeline
2
2
 
3
3
  Config-driven multi-agent pipeline runner for repository work. The installed
4
- package owns the runtime defaults; target repositories use `.pipeline/runs/` for
5
- generated schedules and run artifacts, not as the source of runtime config.
4
+ package owns the runtime defaults. Default Moka runs persist generated
5
+ schedules, run manifests, events, node status, and node results in the configured
6
+ Moka Postgres database, not in the target repository.
6
7
 
7
8
  The published command is `moka`.
8
9
 
@@ -94,8 +95,8 @@ moka explain-plan
94
95
  `moka run "<task>"` is the primary command surface.
95
96
 
96
97
  It runs package-owned workflow config from the current worktree. Scheduled
97
- entrypoints generate a schedule artifact under `.pipeline/runs/<runId>/` and run
98
- the compiled schedule through the runtime.
98
+ entrypoints generate schedule YAML in memory, persist it on the DB-owned run
99
+ manifest, and run the compiled schedule through the runtime.
99
100
 
100
101
  Canonical commands:
101
102
 
@@ -114,7 +115,7 @@ Canonical commands:
114
115
  ```shell
115
116
  moka run "Implement PIPE-123 user-facing behavior"
116
117
  moka run --target local --effort normal "Implement a standard local change"
117
- moka run --schedule .pipeline/runs/<runId>/schedule.yaml "Implement PIPE-123"
118
+ moka run --schedule ./approved-schedule.yaml "Implement PIPE-123"
118
119
  moka run --workflow inspect "Report the app structure and available checks. Do not modify files."
119
120
  moka run --effort quick "Implement a focused fix"
120
121
  moka run --effort normal "Implement a standard fix"
@@ -153,18 +154,24 @@ Read-only ticket commands are `moka ticket graph check`, `moka ticket sequence`,
153
154
  `moka ticket start` without `--dry-run`: they use Backlog CLI task creation and
154
155
  editing or invoke `moka run` for the selected ticket.
155
156
 
156
- Local run artifacts live under `.pipeline/runs/<runId>/`:
157
+ Local run-control state lives in the Moka DB selected by `momokaya.db.url` in
158
+ `~/.config/moka/config.yaml`. The run manifest stores the generated schedule as
159
+ `manifest.schedule`; node results are stored by `(runId, nodeId)` in the durable
160
+ node record table. Use the run-control commands to inspect or export this state:
157
161
 
158
- ```text
159
- .pipeline/runs/<runId>/
160
- schedule.yaml
161
- manifest.json
162
- status.json
163
- events.ndjson
164
- nodes/<node-id>/
165
- artifacts/
162
+ ```shell
163
+ moka runs
164
+ moka status <run-id>
165
+ moka logs <run-id> [node-id]
166
+ moka export <run-id> --sanitize
166
167
  ```
167
168
 
169
+ `moka next node <run-id>` reads the persisted `manifest.schedule` from the Moka
170
+ DB and emits the next ready node envelope. `moka submit-result <run-id>
171
+ <node-id> --json '<RuntimeNodeResult>'` writes the node result back to the same
172
+ DB state. `moka resume <run-id>` reconstructs the graph from the persisted
173
+ schedule; it does not need a repo-local generated schedule file.
174
+
168
175
  Use `moka export <run-id> --sanitize` before sharing a run. The sanitized export
169
176
  keeps portable evidence and omits prompt text, session body content, secrets,
170
177
  tokens, and credentials.
@@ -100,22 +100,22 @@ scheduler:
100
100
  backlog-intake:
101
101
  category: intake
102
102
  profile: moka-researcher
103
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
103
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
104
104
  reasoning_effort: medium
105
105
  red-tests:
106
106
  category: red
107
107
  profile: moka-test-writer
108
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
108
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
109
109
  reasoning_effort: high
110
110
  green-implementation:
111
111
  category: green
112
112
  profile: moka-code-writer
113
- models: [opencode-go/qwen3.7-max, openai/gpt-5.5, kimi-for-coding/k2p6]
113
+ models: [opencode-go/qwen3.7-max, broker/gpt-5.5, kimi-for-coding/k2p6]
114
114
  reasoning_effort: high
115
115
  verification:
116
116
  category: verification
117
117
  profile: moka-verifier
118
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
118
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
119
119
  reasoning_effort: medium
120
120
  execute:
121
121
  required_categories: [intake, research, red, green, mechanical, acceptance, verification, learn]
@@ -123,42 +123,42 @@ scheduler:
123
123
  backlog-intake:
124
124
  category: intake
125
125
  profile: moka-researcher
126
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
126
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
127
127
  reasoning_effort: medium
128
128
  research:
129
129
  category: research
130
130
  profile: moka-researcher
131
- models: [openai/gpt-5.5, kimi-for-coding/k2p6]
131
+ models: [broker/gpt-5.5, kimi-for-coding/k2p6]
132
132
  reasoning_effort: medium
133
133
  red-tests:
134
134
  category: red
135
135
  profile: moka-test-writer
136
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
136
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
137
137
  reasoning_effort: high
138
138
  green-backend:
139
139
  category: green
140
140
  profile: moka-code-writer
141
- models: [opencode-go/qwen3.7-max, openai/gpt-5.5, kimi-for-coding/k2p6]
141
+ models: [opencode-go/qwen3.7-max, broker/gpt-5.5, kimi-for-coding/k2p6]
142
142
  reasoning_effort: high
143
143
  green-frontend:
144
144
  category: green
145
145
  profile: moka-code-writer
146
- models: [openai/gpt-5.5, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
146
+ models: [broker/gpt-5.5, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
147
147
  reasoning_effort: high
148
148
  acceptance-review:
149
149
  category: acceptance
150
150
  profile: moka-acceptance-reviewer
151
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
151
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
152
152
  reasoning_effort: medium
153
153
  verification:
154
154
  category: verification
155
155
  profile: moka-verifier
156
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
156
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
157
157
  reasoning_effort: medium
158
158
  learn:
159
159
  category: learn
160
160
  profile: moka-learner
161
- models: [openai/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
161
+ models: [broker/gpt-5.5, kimi-for-coding/kimi-k2-thinking]
162
162
  reasoning_effort: low
163
163
  schedules:
164
164
  quick-schedule:
@@ -90,7 +90,7 @@ skills:
90
90
  profiles:
91
91
  moka-orchestrator:
92
92
  runner: opencode
93
- model: openai/gpt-5.5
93
+ model: broker/gpt-5.5
94
94
  reasoning_effort: xhigh
95
95
  description: Orchestrate the configured pipeline and enforce gates.
96
96
  instructions: { inline: "Orchestrate through the canonical local `moka run` supervisor. For compatibility slash commands, run the `moka run` command and flags shown in the command body. Treat execution as CLI/supervised runtime, not OpenCode-native Task execution. Enforce only package-configured gates." }
@@ -129,7 +129,7 @@ profiles:
129
129
  repair: { enabled: true, max_attempts: 1 }
130
130
  moka-inspector:
131
131
  runner: opencode
132
- model: openai/gpt-5.5
132
+ model: broker/gpt-5.5
133
133
  reasoning_effort: low
134
134
  description: Inspect the repository without modifying files.
135
135
  instructions: { inline: "Inspect the repository without modifying files." }
@@ -140,7 +140,7 @@ profiles:
140
140
  network: { mode: inherit }
141
141
  moka-schedule-planner:
142
142
  runner: opencode
143
- model: openai/gpt-5.5
143
+ model: broker/gpt-5.5
144
144
  reasoning_effort: xhigh
145
145
  description: Refine a baseline schedule into a specialized approved-plan artifact.
146
146
  instructions: { inline: "Generate exactly one workflow named root as an explicit schedule graph. Return YAML only." }
@@ -224,7 +224,7 @@ profiles:
224
224
  repair: { enabled: true, max_attempts: 1 }
225
225
  moka-learner:
226
226
  runner: opencode
227
- model: openai/gpt-5.5
227
+ model: broker/gpt-5.5
228
228
  reasoning_effort: low
229
229
  description: Store durable lessons from the completed run.
230
230
  instructions: { inline: "Store durable lessons from the completed run when useful." }
@@ -53,7 +53,11 @@ interface SubmitRunnerArgoWorkflowDependencies {
53
53
  kubeConfig?: KubernetesArgoIoDependencies["kubeConfig"];
54
54
  workflowApi?: WorkflowApi;
55
55
  }
56
+ type SubmitDynamicRunnerArgoWorkflowOptions = Omit<SubmitRunnerArgoWorkflowOptions, "scheduleYaml"> & {
57
+ workflowId: string;
58
+ };
56
59
  declare function submitRunnerArgoWorkflow(rawOptions: SubmitRunnerArgoWorkflowOptions, dependencies?: SubmitRunnerArgoWorkflowDependencies): Promise<SubmitRunnerArgoWorkflowResult>;
60
+ declare function submitDynamicRunnerArgoWorkflow(rawOptions: SubmitDynamicRunnerArgoWorkflowOptions, dependencies?: SubmitRunnerArgoWorkflowDependencies): Promise<SubmitRunnerArgoWorkflowResult>;
57
61
  declare function buildCommandScheduleYaml(rawOptions: CommandScheduleOptions): string;
58
62
  //#endregion
59
- export { CommandScheduleOptions, SubmitRunnerArgoWorkflowDependencies, SubmitRunnerArgoWorkflowOptions, SubmitRunnerArgoWorkflowResult, buildCommandScheduleYaml, submitRunnerArgoWorkflow };
63
+ export { CommandScheduleOptions, SubmitDynamicRunnerArgoWorkflowOptions, SubmitRunnerArgoWorkflowDependencies, SubmitRunnerArgoWorkflowOptions, SubmitRunnerArgoWorkflowResult, buildCommandScheduleYaml, submitDynamicRunnerArgoWorkflow, submitRunnerArgoWorkflow };
@@ -4,7 +4,7 @@ import { brokerAuthOptionSchema } from "./credentials/broker.js";
4
4
  import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
5
5
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
6
6
  import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
7
- import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
7
+ import { buildDynamicRunnerArgoWorkflowManifest, buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
8
8
  import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
9
9
  import { KubernetesArgoService, KubernetesArgoServiceLive } from "./runtime/services/kubernetes-argo-service.js";
10
10
  import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
@@ -24,6 +24,10 @@ const configMapSchema = z.object({
24
24
  namespace: z.string().min(1)
25
25
  }).strict()
26
26
  }).strict();
27
+ const createdWorkflowSchema = z.object({ metadata: z.object({
28
+ name: z.string().min(1).optional(),
29
+ uid: z.string().min(1).optional()
30
+ }).passthrough() }).passthrough();
27
31
  const submitRunnerArgoWorkflowOptionsSchema = z.object({
28
32
  brokerAuth: brokerAuthOptionSchema,
29
33
  dbAuth: dbAuthOptionSchema.optional(),
@@ -56,6 +60,9 @@ const commandScheduleOptionsSchema = z.object({
56
60
  function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
57
61
  return Effect.runPromise(Effect.provide(Effect.suspend(() => submitRunnerArgoWorkflowEffect(rawOptions, dependencies)), KubernetesArgoServiceLive));
58
62
  }
63
+ function submitDynamicRunnerArgoWorkflow(rawOptions, dependencies = {}) {
64
+ return Effect.runPromise(Effect.provide(Effect.suspend(() => submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies)), KubernetesArgoServiceLive));
65
+ }
59
66
  function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
60
67
  const { config, ...schemaOptions } = rawOptions;
61
68
  const options = submitRunnerArgoWorkflowOptionsSchema.parse(schemaOptions);
@@ -149,24 +156,94 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
149
156
  namespace: options.namespace,
150
157
  options
151
158
  });
152
- const response = yield* service.createWorkflow({
159
+ return workflowSubmitResult(yield* service.createWorkflow({
153
160
  body: runnerArgoWorkflowManifestSchema.parse(workflow),
154
161
  dependencies,
155
162
  namespace: options.namespace,
156
163
  options
157
- });
158
- const created = z.object({ metadata: z.object({
159
- name: z.string().min(1).optional(),
160
- uid: z.string().min(1).optional()
161
- }).passthrough() }).passthrough().parse(response);
162
- return workflowSubmitResultSchema.parse({
164
+ }), workflow, {
163
165
  namespace: options.namespace,
164
166
  payloadConfigMapName,
165
167
  scheduleConfigMapName: scheduleArtifactConfigMapName,
166
- taskDescriptorConfigMapName,
167
- workflowName: created.metadata.name ?? workflow.metadata.name,
168
- workflowUid: created.metadata.uid
168
+ taskDescriptorConfigMapName
169
+ });
170
+ });
171
+ }
172
+ function submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies) {
173
+ const { config: _config, workflowId, ...schemaOptions } = rawOptions;
174
+ const options = submitRunnerArgoWorkflowOptionsSchema.omit({ scheduleYaml: true }).extend({ workflowId: z.string().min(1) }).parse({
175
+ ...schemaOptions,
176
+ workflowId
177
+ });
178
+ const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
179
+ payload: runnerCommandPayloadSchema.parse(parseRunnerCommandPayload(options.payloadJson)),
180
+ payloadJson: options.payloadJson
181
+ });
182
+ if (payload.workflow.id !== workflowId) throw new Error(`Runner payload workflow '${payload.workflow.id}' does not match dynamic workflow '${workflowId}'`);
183
+ const payloadConfigMapName = `pipeline-payload-${randomBytes(6).toString("hex")}`;
184
+ const labels = {
185
+ "pipeline.oisin.dev/project": payload.run.project,
186
+ "pipeline.oisin.dev/run-id": payload.run.id,
187
+ "pipeline.oisin.dev/source": "argo-workflow",
188
+ "pipeline.oisin.dev/workflow": workflowId
189
+ };
190
+ const workflow = buildDynamicRunnerArgoWorkflowManifest({
191
+ annotations: payload.task.kind === "ticket" ? {
192
+ "pipeline.oisin.dev/ticket-id": payload.task.id,
193
+ "pipeline.oisin.dev/ticket-project": payload.run.project,
194
+ "pipeline.oisin.dev/ticket-title": payload.task.title
195
+ } : {},
196
+ brokerAuth: options.brokerAuth,
197
+ dbAuth: options.dbAuth,
198
+ eventAuthSecretKey: options.eventAuthSecretKey,
199
+ eventAuthSecretName: options.eventAuthSecretName,
200
+ generateName: options.generateName,
201
+ gitCredentialsSecretName: options.gitCredentialsSecretName,
202
+ githubAuthSecretName: options.githubAuthSecretName,
203
+ image: options.image,
204
+ imagePullPolicy: options.imagePullPolicy,
205
+ imagePullSecretName: options.imagePullSecretName,
206
+ labels,
207
+ name: options.name,
208
+ namespace: options.namespace,
209
+ payloadConfigMapName,
210
+ serviceAccountName: options.serviceAccountName,
211
+ workflowId
212
+ });
213
+ return Effect.gen(function* () {
214
+ const service = yield* KubernetesArgoService;
215
+ yield* service.createConfigMap({
216
+ body: configMapSchema.parse({
217
+ apiVersion: "v1",
218
+ data: { "payload.json": payloadJson },
219
+ kind: "ConfigMap",
220
+ metadata: {
221
+ labels,
222
+ name: payloadConfigMapName,
223
+ namespace: options.namespace
224
+ }
225
+ }),
226
+ dependencies,
227
+ namespace: options.namespace,
228
+ options
169
229
  });
230
+ return workflowSubmitResult(yield* service.createWorkflow({
231
+ body: runnerArgoWorkflowManifestSchema.parse(workflow),
232
+ dependencies,
233
+ namespace: options.namespace,
234
+ options
235
+ }), workflow, {
236
+ namespace: options.namespace,
237
+ payloadConfigMapName
238
+ });
239
+ });
240
+ }
241
+ function workflowSubmitResult(response, workflow, base) {
242
+ const created = createdWorkflowSchema.parse(response);
243
+ return workflowSubmitResultSchema.parse({
244
+ ...base,
245
+ workflowName: created.metadata.name ?? workflow.metadata.name,
246
+ workflowUid: created.metadata.uid
170
247
  });
171
248
  }
172
249
  function buildCommandScheduleYaml(rawOptions) {
@@ -209,4 +286,4 @@ function compileSubmitArgoGraph(compiled) {
209
286
  });
210
287
  }
211
288
  //#endregion
212
- export { buildCommandScheduleYaml, submitRunnerArgoWorkflow };
289
+ export { buildCommandScheduleYaml, submitDynamicRunnerArgoWorkflow, submitRunnerArgoWorkflow };
@@ -1,5 +1,5 @@
1
1
  import { WorkflowExecutionPlan } from "./planning/compile.js";
2
- import { buildRunnerArgoWorkflowOptionsSchema } from "./remote/argo/model.js";
2
+ import { buildDynamicRunnerArgoWorkflowOptionsSchema, buildRunnerArgoWorkflowOptionsSchema } from "./remote/argo/model.js";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/argo-workflow.d.ts
@@ -66,10 +66,16 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
66
66
  name: z.ZodString;
67
67
  path: z.ZodOptional<z.ZodString>;
68
68
  }, z.core.$strict>>>;
69
+ parameters: z.ZodOptional<z.ZodArray<z.ZodObject<{
70
+ name: z.ZodString;
71
+ value: z.ZodString;
72
+ }, z.core.$strict>>>;
69
73
  }, z.core.$strict>>;
70
74
  dependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
71
75
  name: z.ZodString;
72
76
  template: z.ZodString;
77
+ when: z.ZodOptional<z.ZodString>;
78
+ withParam: z.ZodOptional<z.ZodString>;
73
79
  }, z.core.$strict>>;
74
80
  }, z.core.$strict>>;
75
81
  inputs: z.ZodOptional<z.ZodObject<{
@@ -83,12 +89,30 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
83
89
  }, z.core.$strict>>>;
84
90
  }, z.core.$strict>>;
85
91
  outputs: z.ZodOptional<z.ZodObject<{
86
- artifacts: z.ZodArray<z.ZodObject<{
92
+ artifacts: z.ZodOptional<z.ZodArray<z.ZodObject<{
87
93
  from: z.ZodOptional<z.ZodString>;
88
94
  name: z.ZodString;
89
95
  path: z.ZodOptional<z.ZodString>;
90
- }, z.core.$strict>>;
96
+ }, z.core.$strict>>>;
97
+ parameters: z.ZodOptional<z.ZodArray<z.ZodObject<{
98
+ name: z.ZodString;
99
+ valueFrom: z.ZodObject<{
100
+ path: z.ZodString;
101
+ }, z.core.$strict>;
102
+ }, z.core.$strict>>>;
91
103
  }, z.core.$strict>>;
104
+ steps: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
105
+ arguments: z.ZodOptional<z.ZodObject<{
106
+ parameters: z.ZodOptional<z.ZodArray<z.ZodObject<{
107
+ name: z.ZodString;
108
+ value: z.ZodString;
109
+ }, z.core.$strict>>>;
110
+ }, z.core.$strict>>;
111
+ name: z.ZodString;
112
+ template: z.ZodString;
113
+ when: z.ZodOptional<z.ZodString>;
114
+ withParam: z.ZodOptional<z.ZodString>;
115
+ }, z.core.$strict>>>>;
92
116
  activeDeadlineSeconds: z.ZodOptional<z.ZodNumber>;
93
117
  name: z.ZodString;
94
118
  retryStrategy: z.ZodOptional<z.ZodObject<{
@@ -132,7 +156,9 @@ type ArgoWorkflowManifest = z.infer<typeof runnerArgoWorkflowManifestSchema>;
132
156
  type BuildRunnerArgoWorkflowOptions = z.input<typeof buildRunnerArgoWorkflowOptionsSchema> & {
133
157
  plan: WorkflowExecutionPlan;
134
158
  };
159
+ type BuildDynamicRunnerArgoWorkflowOptions = z.input<typeof buildDynamicRunnerArgoWorkflowOptionsSchema>;
135
160
  declare function buildRunnerArgoWorkflowManifest(rawOptions: BuildRunnerArgoWorkflowOptions): ArgoWorkflowManifest;
161
+ declare function buildDynamicRunnerArgoWorkflowManifest(rawOptions: BuildDynamicRunnerArgoWorkflowOptions): ArgoWorkflowManifest;
136
162
  declare function stringifyRunnerArgoWorkflow(workflow: ArgoWorkflowManifest): string;
137
163
  //#endregion
138
- export { ArgoWorkflowManifest, BuildRunnerArgoWorkflowOptions, buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema, stringifyRunnerArgoWorkflow };
164
+ export { ArgoWorkflowManifest, BuildDynamicRunnerArgoWorkflowOptions, BuildRunnerArgoWorkflowOptions, buildDynamicRunnerArgoWorkflowManifest, buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema, stringifyRunnerArgoWorkflow };
@@ -1,8 +1,8 @@
1
1
  import { compileArgoExecutionGraph } from "./argo-graph.js";
2
2
  import { RUNNER_WORKFLOW_ENTRYPOINT, RUNNER_WORKFLOW_START_TASK } from "./remote/argo/policy.js";
3
- import { buildRunnerArgoWorkflowOptionsSchema, createRunnerArgoWorkflowManifestSchema } from "./remote/argo/model.js";
4
- import { runnerWorkflowStorage } from "./remote/argo/storage.js";
5
- import { runnerCommandTemplate, runnerFinalizerTemplate, runnerLifecycleTemplate } from "./remote/argo/templates.js";
3
+ import { buildDynamicRunnerArgoWorkflowOptionsSchema, buildRunnerArgoWorkflowOptionsSchema, createRunnerArgoWorkflowManifestSchema } from "./remote/argo/model.js";
4
+ import { dynamicRunnerWorkflowStorage, runnerWorkflowStorage } from "./remote/argo/storage.js";
5
+ import { READY_NODE_IDS_PARAMETER, dynamicPreScheduleTemplate, dynamicReadyWaveSelectorTemplate, dynamicRunnerCommandTemplate, dynamicRunnerFinalizerTemplate, runnerCommandTemplate, runnerFinalizerTemplate, runnerLifecycleTemplate } from "./remote/argo/templates.js";
6
6
  import { stringify } from "yaml";
7
7
  //#region src/argo-workflow.ts
8
8
  const runnerArgoWorkflowManifestSchema = createRunnerArgoWorkflowManifestSchema();
@@ -17,6 +17,16 @@ function buildRunnerArgoWorkflowManifest(rawOptions) {
17
17
  spec: workflowSpec(options, graph.tasks, storage)
18
18
  });
19
19
  }
20
+ function buildDynamicRunnerArgoWorkflowManifest(rawOptions) {
21
+ const options = buildDynamicRunnerArgoWorkflowOptionsSchema.parse(rawOptions);
22
+ const storage = dynamicRunnerWorkflowStorage(options);
23
+ return runnerArgoWorkflowManifestSchema.parse({
24
+ apiVersion: "argoproj.io/v1alpha1",
25
+ kind: "Workflow",
26
+ metadata: dynamicWorkflowMetadata(options),
27
+ spec: dynamicWorkflowSpec(options, storage)
28
+ });
29
+ }
20
30
  function stringifyRunnerArgoWorkflow(workflow) {
21
31
  return stringify(runnerArgoWorkflowManifestSchema.parse(workflow));
22
32
  }
@@ -40,6 +50,19 @@ function workflowMetadata(options) {
40
50
  namespace: options.namespace
41
51
  };
42
52
  }
53
+ function dynamicWorkflowMetadata(options) {
54
+ return {
55
+ annotations: compactRecord(options.annotations),
56
+ ...options.name ? { name: options.name } : {},
57
+ ...options.generateName ? { generateName: options.generateName } : {},
58
+ labels: compactRecord({
59
+ "pipeline.oisin.dev/source": "argo-workflow",
60
+ "pipeline.oisin.dev/workflow": options.workflowId,
61
+ ...options.labels
62
+ }),
63
+ namespace: options.namespace
64
+ };
65
+ }
43
66
  function workflowSpec(options, tasks, storage) {
44
67
  return {
45
68
  ...options.activeDeadlineSeconds ? { activeDeadlineSeconds: options.activeDeadlineSeconds } : {},
@@ -52,6 +75,18 @@ function workflowSpec(options, tasks, storage) {
52
75
  volumes: storage.volumes
53
76
  };
54
77
  }
78
+ function dynamicWorkflowSpec(options, storage) {
79
+ return {
80
+ ...options.activeDeadlineSeconds ? { activeDeadlineSeconds: options.activeDeadlineSeconds } : {},
81
+ entrypoint: RUNNER_WORKFLOW_ENTRYPOINT,
82
+ ...options.imagePullSecretName ? { imagePullSecrets: [{ name: options.imagePullSecretName }] } : {},
83
+ serviceAccountName: options.serviceAccountName,
84
+ onExit: "pipeline-finalizer",
85
+ templates: dynamicWorkflowTemplates(options, storage.volumeMounts),
86
+ ...options.ttlStrategy ? { ttlStrategy: options.ttlStrategy } : {},
87
+ volumes: storage.volumes
88
+ };
89
+ }
55
90
  function workflowTemplates(options, tasks, volumeMounts) {
56
91
  return [
57
92
  workflowDagTemplate(tasks),
@@ -60,6 +95,18 @@ function workflowTemplates(options, tasks, volumeMounts) {
60
95
  runnerFinalizerTemplate(options, volumeMounts)
61
96
  ];
62
97
  }
98
+ function dynamicWorkflowTemplates(options, volumeMounts) {
99
+ return [
100
+ dynamicWorkflowEntrypointTemplate(),
101
+ dynamicDrainTemplate(),
102
+ dynamicPreScheduleTemplate("pre-research", options, volumeMounts),
103
+ dynamicPreScheduleTemplate("pre-planning", options, volumeMounts),
104
+ dynamicPreScheduleTemplate("generate-schedule", options, volumeMounts),
105
+ dynamicReadyWaveSelectorTemplate(options, volumeMounts),
106
+ dynamicRunnerCommandTemplate(options, volumeMounts),
107
+ dynamicRunnerFinalizerTemplate(options, volumeMounts)
108
+ ];
109
+ }
63
110
  function workflowDagTemplate(tasks) {
64
111
  return {
65
112
  dag: { tasks: [{
@@ -73,8 +120,58 @@ function workflowDagTemplate(tasks) {
73
120
  name: RUNNER_WORKFLOW_ENTRYPOINT
74
121
  };
75
122
  }
123
+ function dynamicWorkflowEntrypointTemplate() {
124
+ return {
125
+ name: RUNNER_WORKFLOW_ENTRYPOINT,
126
+ steps: [
127
+ [{
128
+ name: "pre-research",
129
+ template: "pre-research"
130
+ }],
131
+ [{
132
+ name: "pre-planning",
133
+ template: "pre-planning"
134
+ }],
135
+ [{
136
+ name: "generate-schedule",
137
+ template: "generate-schedule"
138
+ }],
139
+ [{
140
+ name: "drain-ready-waves",
141
+ template: "drain-ready-waves"
142
+ }]
143
+ ]
144
+ };
145
+ }
146
+ function dynamicDrainTemplate() {
147
+ const readyExpression = `{{steps.select-ready-wave.outputs.parameters.${READY_NODE_IDS_PARAMETER}}} != []`;
148
+ return {
149
+ name: "drain-ready-waves",
150
+ steps: [
151
+ [{
152
+ name: "select-ready-wave",
153
+ template: "select-ready-wave"
154
+ }],
155
+ [{
156
+ arguments: { parameters: [{
157
+ name: "node-id",
158
+ value: "{{item}}"
159
+ }] },
160
+ name: "run-ready-node",
161
+ template: "runner-command",
162
+ when: readyExpression,
163
+ withParam: `{{steps.select-ready-wave.outputs.parameters.${READY_NODE_IDS_PARAMETER}}}`
164
+ }],
165
+ [{
166
+ name: "drain-next-wave",
167
+ template: "drain-ready-waves",
168
+ when: readyExpression
169
+ }]
170
+ ]
171
+ };
172
+ }
76
173
  function compactRecord(input) {
77
174
  return Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== void 0));
78
175
  }
79
176
  //#endregion
80
- export { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema, stringifyRunnerArgoWorkflow };
177
+ export { buildDynamicRunnerArgoWorkflowManifest, buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema, stringifyRunnerArgoWorkflow };
@@ -1,6 +1,8 @@
1
1
  import { runRunnerCommand } from "../runner-command/run.js";
2
2
  import { runRunnerFinalize } from "../runner-command/finalize.js";
3
3
  import { runRunnerLifecycle } from "../runner-command/lifecycle.js";
4
+ import { runPreSchedulePhase } from "../runner-command/pre-schedule.js";
5
+ import { runSelectReadyWave } from "../runner-command/select-ready-wave.js";
4
6
  import { Context, Effect, Layer } from "effect";
5
7
  //#region src/commands/runner-command-command.ts
6
8
  var RunnerCommandService = class extends Context.Service()("RunnerCommandService") {};
@@ -13,9 +15,17 @@ const RunnerCommandServiceLive = Layer.succeed(RunnerCommandService, {
13
15
  catch: (error) => error,
14
16
  try: () => runRunnerLifecycle(options)
15
17
  }),
18
+ preSchedule: (options) => Effect.tryPromise({
19
+ catch: (error) => error,
20
+ try: () => runPreSchedulePhase(options)
21
+ }),
16
22
  run: (options) => Effect.tryPromise({
17
23
  catch: (error) => error,
18
24
  try: () => runRunnerCommand(options)
25
+ }),
26
+ selectReadyWave: (options) => Effect.tryPromise({
27
+ catch: (error) => error,
28
+ try: () => runSelectReadyWave(options)
19
29
  })
20
30
  });
21
31
  const setProcessExitCode = (exitCode) => Effect.sync(() => {
@@ -30,11 +40,19 @@ const runRunnerLifecycleEffect = (options) => Effect.gen(function* () {
30
40
  const runRunnerFinalizeEffect = (options) => Effect.gen(function* () {
31
41
  yield* setProcessExitCode(yield* (yield* RunnerCommandService).finalize(options));
32
42
  });
43
+ const runPreScheduleEffect = (options) => Effect.gen(function* () {
44
+ yield* setProcessExitCode(yield* (yield* RunnerCommandService).preSchedule(options));
45
+ });
46
+ const runSelectReadyWaveEffect = (options) => Effect.gen(function* () {
47
+ yield* setProcessExitCode(yield* (yield* RunnerCommandService).selectReadyWave(options));
48
+ });
33
49
  const runRunnerProgram = (program) => Effect.runPromise(Effect.provide(program, RunnerCommandServiceLive));
34
50
  function registerRunnerCommandCommand(program) {
35
- program.command("runner-command").description("Run one scheduled Argo Workflow task").requiredOption("--payload-file <path>", "Path to the runner payload JSON").requiredOption("--schedule-file <path>", "Path to the schedule artifact YAML").action((options) => runRunnerProgram(runRunnerCommandEffect(options)));
51
+ program.command("runner-command").description("Run one scheduled Argo Workflow task").requiredOption("--payload-file <path>", "Path to the runner payload JSON").option("--node-id <id>", "Node id to execute without a task descriptor").option("--schedule-file <path>", "Path to the schedule artifact YAML").option("--schedule-source <source>", "Schedule source: file or db").action((options) => runRunnerProgram(runRunnerCommandEffect(options)));
36
52
  program.command("runner-lifecycle").description("Run one Argo Workflow lifecycle phase").requiredOption("--phase <phase>", "Lifecycle phase to run").requiredOption("--payload-file <path>", "Path to the runner payload JSON").requiredOption("--schedule-file <path>", "Path to the schedule artifact YAML").action((options) => runRunnerProgram(runRunnerLifecycleEffect(options)));
37
- program.command("runner-finalize").description("Finalize one Argo Workflow run").requiredOption("--payload-file <path>", "Path to the runner payload JSON").requiredOption("--schedule-file <path>", "Path to the schedule artifact YAML").requiredOption("--argo-status <status>", "Argo Workflow status").action((options) => runRunnerProgram(runRunnerFinalizeEffect(options)));
53
+ program.command("runner-pre-schedule").description("Run one dynamic pre-schedule phase").requiredOption("--phase <phase>", "pre-research, pre-planning, or generate-schedule").requiredOption("--payload-file <path>", "Path to the runner payload JSON").action((options) => runRunnerProgram(runPreScheduleEffect(options)));
54
+ program.command("runner-finalize").description("Finalize one Argo Workflow run").requiredOption("--payload-file <path>", "Path to the runner payload JSON").option("--schedule-file <path>", "Path to the schedule artifact YAML").option("--schedule-source <source>", "Schedule source: file or db").requiredOption("--argo-status <status>", "Argo Workflow status").action((options) => runRunnerProgram(runRunnerFinalizeEffect(options)));
55
+ program.command("runner-select-ready-wave").description("Select DB-ready nodes for the next dynamic Argo wave").requiredOption("--payload-file <path>", "Path to the runner payload JSON").requiredOption("--output-file <path>", "Path where the ready node id JSON array is written").action((options) => runRunnerProgram(runSelectReadyWaveEffect(options)));
38
56
  }
39
57
  //#endregion
40
58
  export { registerRunnerCommandCommand };
@@ -95,8 +95,8 @@ declare const mokaSubmitHookPolicySchema: z.ZodObject<{
95
95
  declare const mokaSubmitResultSchema: z.ZodObject<{
96
96
  namespace: z.ZodString;
97
97
  payloadConfigMapName: z.ZodString;
98
- scheduleConfigMapName: z.ZodString;
99
- taskDescriptorConfigMapName: z.ZodString;
98
+ scheduleConfigMapName: z.ZodOptional<z.ZodString>;
99
+ taskDescriptorConfigMapName: z.ZodOptional<z.ZodString>;
100
100
  workflowName: z.ZodString;
101
101
  workflowUid: z.ZodOptional<z.ZodString>;
102
102
  }, z.core.$strict>;
@@ -1,6 +1,7 @@
1
1
  import { PipelineConfig } from "../config/schemas.js";
2
2
  import { WorkflowExecutionPlan } from "./compile.js";
3
3
  import { AgentResult, RunnerExecutionOptions, RunnerLaunchPlan } from "../runner.js";
4
+ import { TicketPlan } from "../tickets/ticket-plan.js";
4
5
  import { z } from "zod";
5
6
 
6
7
  //#region src/planning/generate.d.ts
@@ -1591,6 +1592,7 @@ interface GenerateScheduleOptions {
1591
1592
  entrypointId: string;
1592
1593
  executor?: (plan: RunnerLaunchPlan, options: RunnerExecutionOptions) => AgentResult | Promise<AgentResult>;
1593
1594
  generatedAt?: Date;
1595
+ phaseContext?: SchedulePhaseContext;
1594
1596
  runId?: string;
1595
1597
  task: string;
1596
1598
  worktreePath: string;
@@ -1615,8 +1617,20 @@ interface BacklogWorkUnit {
1615
1617
  }
1616
1618
  interface SchedulePlanningContext {
1617
1619
  parentWorkUnits: BacklogWorkUnit[];
1620
+ research?: ScheduleResearchContext;
1618
1621
  workUnits: BacklogWorkUnit[];
1619
1622
  }
1623
+ interface ScheduleResearchContext {
1624
+ ac: string[];
1625
+ files?: string[];
1626
+ findings: string[];
1627
+ risks?: string[];
1628
+ target?: string;
1629
+ }
1630
+ interface SchedulePhaseContext {
1631
+ research?: ScheduleResearchContext;
1632
+ ticketPlan?: TicketPlan;
1633
+ }
1620
1634
  /**
1621
1635
  * A backlog ticket's frontmatter may declare dependencies on sibling tickets
1622
1636
  * that are not part of this run (e.g. submitting TOVA-766.03 alone when its
@@ -1640,6 +1654,12 @@ declare class ScheduleArtifactError extends ScheduleArtifactError_base<{
1640
1654
  declare function compileScheduleArtifact(config: PipelineConfig, artifact: ScheduleArtifact, projectRoot?: string): CompiledScheduleArtifact;
1641
1655
  declare function generateScheduleArtifact(options: GenerateScheduleOptions): Promise<GenerateScheduleResult>;
1642
1656
  declare function generateScheduleArtifactInMemory(options: GenerateScheduleOptions): Promise<GenerateScheduleInMemoryResult>;
1657
+ declare function schedulePlanningContext(input: {
1658
+ phaseContext?: SchedulePhaseContext;
1659
+ task: string;
1660
+ worktreePath: string;
1661
+ }): SchedulePlanningContext;
1662
+ declare function ticketPlanPlanningContext(plan: TicketPlan): Pick<SchedulePlanningContext, "parentWorkUnits" | "workUnits">;
1643
1663
  declare function scheduleArtifactPath(worktreePath: string, scheduleId: string): string;
1644
1664
  //#endregion
1645
- export { BacklogWorkUnit, CompiledScheduleArtifact, GenerateScheduleInMemoryResult, GenerateScheduleOptions, GenerateScheduleResult, ScheduleArtifact, ScheduleArtifactError, SchedulePlanningContext, compileScheduleArtifact, generateScheduleArtifact, generateScheduleArtifactInMemory, parseScheduleArtifact, pruneOutOfScopeDependencies, scheduleArtifactPath };
1665
+ export { BacklogWorkUnit, CompiledScheduleArtifact, GenerateScheduleInMemoryResult, GenerateScheduleOptions, GenerateScheduleResult, ScheduleArtifact, ScheduleArtifactError, SchedulePhaseContext, SchedulePlanningContext, ScheduleResearchContext, compileScheduleArtifact, generateScheduleArtifact, generateScheduleArtifactInMemory, parseScheduleArtifact, pruneOutOfScopeDependencies, scheduleArtifactPath, schedulePlanningContext, ticketPlanPlanningContext };