@oisincoveney/pipeline 2.1.1 → 2.3.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 (64) hide show
  1. package/.agents/skills/orchestrate/SKILL.md +80 -0
  2. package/defaults/pipeline.yaml +111 -0
  3. package/defaults/profiles.yaml +215 -0
  4. package/defaults/runners.yaml +24 -0
  5. package/dist/argo-graph.js +59 -27
  6. package/dist/argo-submit.d.ts +0 -1
  7. package/dist/argo-submit.js +11 -6
  8. package/dist/argo-workflow.d.ts +1 -2
  9. package/dist/argo-workflow.js +0 -2
  10. package/dist/claude-settings-config.js +44 -0
  11. package/dist/cli/program.js +12 -5
  12. package/dist/cli/submit-options.js +1 -2
  13. package/dist/cluster-doctor.js +0 -12
  14. package/dist/commands/pipeline-command.js +1 -1
  15. package/dist/config/defaults.js +7 -350
  16. package/dist/config/schemas.d.ts +9 -9
  17. package/dist/install-commands/claude-code.js +160 -0
  18. package/dist/install-commands/opencode.js +56 -39
  19. package/dist/install-commands/shared.js +32 -5
  20. package/dist/install-commands.js +26 -15
  21. package/dist/json-config-merge.js +47 -0
  22. package/dist/mcp/gateway.js +9 -1
  23. package/dist/moka-global-config.d.ts +0 -1
  24. package/dist/moka-global-config.js +0 -1
  25. package/dist/moka-submit.d.ts +8 -11
  26. package/dist/moka-submit.js +1 -4
  27. package/dist/opencode-project-config.js +2 -45
  28. package/dist/pipeline-runtime.d.ts +9 -0
  29. package/dist/pipeline-runtime.js +50 -8
  30. package/dist/planned-node.js +2 -5
  31. package/dist/{workflow-planner.d.ts → planning/compile.d.ts} +2 -2
  32. package/dist/{workflow-planner.js → planning/compile.js} +6 -83
  33. package/dist/{schedule/planner.d.ts → planning/generate.d.ts} +17 -3
  34. package/dist/{schedule/planner.js → planning/generate.js} +24 -56
  35. package/dist/planning/graph.js +138 -0
  36. package/dist/runner-command/lifecycle-context.js +2 -3
  37. package/dist/runner-command/run.js +2 -3
  38. package/dist/runner-command-contract.d.ts +2 -2
  39. package/dist/runner-event-schema.d.ts +349 -0
  40. package/dist/runner-event-schema.js +185 -0
  41. package/dist/runner-output.js +2 -2
  42. package/dist/runner.d.ts +29 -0
  43. package/dist/runtime/agent-node/agent-node.js +1 -0
  44. package/dist/runtime/context/context.js +1 -1
  45. package/dist/runtime/contracts/contracts.d.ts +18 -1
  46. package/dist/runtime/node-state-store.js +7 -0
  47. package/dist/runtime/opencode-adapter.js +28 -8
  48. package/dist/runtime/opencode-agent-name.js +18 -0
  49. package/dist/runtime/opencode-runtime.js +62 -0
  50. package/dist/runtime/opencode-server.js +67 -0
  51. package/dist/runtime/opencode-session-executor.js +206 -0
  52. package/dist/schedule/passes/coverage.js +7 -51
  53. package/dist/schedule/passes/ids.js +3 -23
  54. package/dist/schedule/scheduling-roles.js +19 -0
  55. package/dist/strings.js +30 -1
  56. package/docs/adr-opencode-first-goal-loop-runtime.md +1 -1
  57. package/docs/config-architecture.md +43 -6
  58. package/docs/mcp-gateway.md +4 -4
  59. package/docs/operator-guide.md +9 -8
  60. package/docs/pipeline-console-runner-contract.md +3 -4
  61. package/docs/slash-command-adapter-contract.md +1 -0
  62. package/package.json +10 -5
  63. package/dist/schedule-planner.d.ts +0 -2
  64. package/dist/schedule-planner.js +0 -2
@@ -0,0 +1,80 @@
1
+ ---
2
+ description: Local multi-agent orchestrator — decompose a task and fan it out to the MoKa specialist roster on the current machine instead of submitting to the remote Moka pipeline. The local twin of the MoKa Orchestrator. On Claude Code, dispatch each agent with `opencode run`; on OpenCode, spawn native Task subagents. Use when the user wants parallel specialist agents driven locally rather than as Argo/k8s jobs.
3
+ name: orchestrate
4
+ ---
5
+
6
+ # Orchestrate
7
+
8
+ The **local twin of the MoKa Orchestrator**. The MoKa Orchestrator decomposes a task and submits a schedule to Argo/k8s via `moka submit`, where the runtime executes it as DAG jobs on the cluster. Orchestrate runs the **same roster, same loop, on the current machine** — no schedule, no `moka submit`, no Argo. It is the hands-on, here-and-now path for getting work done through specialist agents.
9
+
10
+ Use this skill when the user wants real work driven through **parallel specialist agents locally**: a task large enough to decompose into research / test / implement / verify lanes, where you stay the controller and the agents do the labor.
11
+
12
+ ## When NOT to use
13
+
14
+ - **Durable, reproducible, or cluster-scale runs** → use [[quick]] or [[execute]] (these submit through `moka submit`). Orchestrate is ephemeral and local; it leaves no schedule artifact.
15
+ - **Trivial single-threaded work** → just do it inline. Spawning agents for a one-line change is pure overhead.
16
+ - **You need package gates enforced as part of a pipeline run** → that is the remote path's job. Orchestrate still *uses* the gate agents (Verifier, Acceptance Reviewer) but does not replace pipeline-level gating.
17
+
18
+ ## The roster
19
+
20
+ The same specialist agents the MoKa pipeline uses, mirrored locally. Each is `mode: all`, so it works both as an `opencode run --agent` subprocess and as a native Task subagent.
21
+
22
+ | Role | Agent name | Writes | Job |
23
+ |-------------|-------------------------|---------------|-----|
24
+ | Research | `MoKa Researcher` | `research.json` only | Read-only; map the codebase, gather context, extract acceptance criteria. |
25
+ | Test | `MoKa Test Writer` | `*.test.ts` only | Write failing tests that describe the desired behaviour. |
26
+ | Implement | `MoKa Code Writer` | `src/**` only | Smallest production change that makes the failing tests pass. |
27
+ | Verify | `MoKa Verifier` | nothing | Run checks, judge diff against AC, emit `PASS`/`FAIL` with evidence. |
28
+ | Review | `MoKa Acceptance Reviewer` | nothing | Acceptance/quality gate before declaring done. |
29
+ | Inspect | `MoKa Inspector` | nothing | Read-only repository inspection / explanation. |
30
+
31
+ Keep each agent inside its lane — never ask the Code Writer to touch tests, or the Verifier to write files. The lane boundaries are what make fan-out safe.
32
+
33
+ ## Dispatch by host
34
+
35
+ The orchestration **doctrine below is identical on every host**. Only the spawn mechanism differs — select the branch for the host you are actually running in.
36
+
37
+ ### On Claude Code → `opencode run`
38
+
39
+ Spawn each roster member as a headless OpenCode subprocess and read its JSON back:
40
+
41
+ ```sh
42
+ opencode run --agent "MoKa Code Writer" --format json \
43
+ "<scoped task + acceptance criteria + paths to read>"
44
+ ```
45
+
46
+ - Select the roster member with `--agent "<exact name>"` (names from the table above).
47
+ - Use `--format json` so the agent's structured result comes back machine-readable; parse it, do not eyeball it.
48
+ - **Parallelize independent lanes**: launch each `opencode run` as a background Bash process (one tool call per lane in the same turn), then collect. Run dependent lanes only after their inputs land.
49
+ - Pass context by path, not by paste — agents read the worktree directly. Hand them the files/AC the Researcher produced.
50
+ - `--model` / `--variant` only when a lane genuinely needs a different tier; otherwise inherit.
51
+
52
+ ### On OpenCode → native Task subagents
53
+
54
+ You are already inside OpenCode — do not shell out to `opencode run`. Spawn the roster directly with the native **Task** tool, selecting the agent by the same name:
55
+
56
+ - `task` → `MoKa Researcher`, `MoKa Test Writer`, `MoKa Code Writer`, `MoKa Verifier`, `MoKa Acceptance Reviewer`.
57
+ - Issue independent Task calls together so they run concurrently; sequence dependent ones.
58
+ - Each subagent's structured output returns to you as the controller — gather, do not re-do their work.
59
+
60
+ ## The loop
61
+
62
+ Whichever host you are on, run the same five steps:
63
+
64
+ 1. **Plan** — Decompose the task into a DAG of agent lanes. Model parallelism *structurally*: independent lanes fan out together, dependents wait on their inputs (research → tests → implementation → verify). Do not invent JSON-pointer fanout; nest the work as real lanes.
65
+ 2. **Dispatch** — Fan out per the host branch above. Scope each agent tightly: one job, its lane's write boundary, explicit acceptance criteria.
66
+ 3. **Gather** — Collect each agent's structured output (`research.json`, the Verifier's verdict JSON, etc.). Treat the returned artifact as the source of truth.
67
+ 4. **Gate** — Run `MoKa Verifier`, then `MoKa Acceptance Reviewer`. Do **not** accept work on a `FAIL`. Loop the relevant lane (re-dispatch Code Writer with the failure evidence) rather than papering over it.
68
+ 5. **Synthesize** — Report only the evidence the agents actually returned: what passed, what the diff is, what the verifier proved. Never fabricate or assume an outcome an agent did not report.
69
+
70
+ ## Rules
71
+
72
+ - **Doctrine is host-neutral; only the Dispatch section is host-specific.** Do not leak `opencode run` syntax into an OpenCode run or Task-tool talk into a Claude run.
73
+ - **You are the controller, not a worker.** Decompose, dispatch, gate, synthesize. Let the specialists do the labor inside their lanes.
74
+ - **Evidence only.** Report what agents returned. A green claim needs a Verifier `PASS` with evidence behind it — see [[verify]].
75
+ - **Respect lane write boundaries.** Researcher/Verifier/Reviewer write nothing; Test Writer touches only tests; Code Writer touches only `src/`. Mixed lanes corrupt parallel fan-out.
76
+ - **Local, not durable.** If the user needs a reproducible cluster run or a schedule artifact, route to [[quick]] / [[execute]] instead.
77
+
78
+ ## The short version
79
+
80
+ Orchestrate is `moka submit` brought home: same roster, same decompose → dispatch → gather → gate → synthesize loop, run on this machine. On Claude Code each agent is an `opencode run --agent` subprocess; on OpenCode each is a native Task subagent. You stay the orchestrator — fan out the lanes, gate on real verifier evidence, and report only what the agents proved.
@@ -0,0 +1,111 @@
1
+ version: 1
2
+ default_workflow: inspect
3
+ orchestrator:
4
+ profile: moka-orchestrator
5
+ entrypoints:
6
+ quick:
7
+ schedule: quick-schedule
8
+ description: Compact planner-generated pipeline for small work
9
+ execute:
10
+ schedule: execute-schedule
11
+ description: Full planner-generated pipeline for repository work
12
+ inspect:
13
+ workflow: inspect
14
+ description: Read-only repository inspection
15
+ hooks:
16
+ functions:
17
+ generated-defaults-audit:
18
+ kind: command
19
+ command: [node, -e, "const fs=require('node:fs'); fs.writeFileSync(process.env.PIPELINE_HOOK_RESULT, JSON.stringify({status:'pass',summary:'Generated defaults audit passed'}));"]
20
+ trusted: true
21
+ timeout_ms: 5000
22
+ output_limit_bytes: 4096
23
+ on:
24
+ workflow.start:
25
+ - id: generated-defaults-audit
26
+ function: generated-defaults-audit
27
+ failure: fail
28
+ runner_command:
29
+ environment:
30
+ setup:
31
+ - command: bun
32
+ args: [install, --frozen-lockfile]
33
+ scheduler:
34
+ commands:
35
+ quick:
36
+ schedule: quick-schedule
37
+ catalog: quick
38
+ execute:
39
+ schedule: execute-schedule
40
+ catalog: execute
41
+ node_catalogs:
42
+ quick:
43
+ required_categories: [intake, red, green, mechanical, verification]
44
+ nodes:
45
+ backlog-intake:
46
+ category: intake
47
+ profile: moka-researcher
48
+ models: [openai/gpt-5.5-medium]
49
+ red-tests:
50
+ category: red
51
+ profile: moka-test-writer
52
+ models: [openai/gpt-5.5-high, kimi-for-coding/kimi-k2-thinking]
53
+ green-implementation:
54
+ category: green
55
+ profile: moka-code-writer
56
+ models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
57
+ verification:
58
+ category: verification
59
+ profile: moka-verifier
60
+ models: [openai/gpt-5.5-medium]
61
+ execute:
62
+ required_categories: [intake, research, red, green, mechanical, acceptance, verification, learn]
63
+ nodes:
64
+ backlog-intake:
65
+ category: intake
66
+ profile: moka-researcher
67
+ models: [openai/gpt-5.5-medium]
68
+ research:
69
+ category: research
70
+ profile: moka-researcher
71
+ models: [openai/gpt-5.5-medium, kimi-for-coding/k2p6]
72
+ red-tests:
73
+ category: red
74
+ profile: moka-test-writer
75
+ models: [openai/gpt-5.5-high, kimi-for-coding/kimi-k2-thinking]
76
+ green-backend:
77
+ category: green
78
+ profile: moka-code-writer
79
+ models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
80
+ green-frontend:
81
+ category: green
82
+ profile: moka-code-writer
83
+ models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
84
+ acceptance-review:
85
+ category: acceptance
86
+ profile: moka-acceptance-reviewer
87
+ models: [openai/gpt-5.5-medium]
88
+ verification:
89
+ category: verification
90
+ profile: moka-verifier
91
+ models: [openai/gpt-5.5-medium]
92
+ learn:
93
+ category: learn
94
+ profile: moka-learner
95
+ models: [openai/gpt-5.5-low]
96
+ schedules:
97
+ quick-schedule:
98
+ baseline: quick
99
+ planner_profile: moka-schedule-planner
100
+ node_catalog: quick
101
+ execute-schedule:
102
+ baseline: execute
103
+ planner_profile: moka-schedule-planner
104
+ node_catalog: execute
105
+ workflows:
106
+ inspect:
107
+ description: Read-only repository inspection workflow.
108
+ nodes:
109
+ - id: inspect
110
+ kind: agent
111
+ profile: moka-inspector
@@ -0,0 +1,215 @@
1
+ version: 1
2
+ mcp_gateway:
3
+ provider: toolhive
4
+ mode: local
5
+ url: https://pipeline-mcp.momokaya.ee/mcp/
6
+ url_env: PIPELINE_MCP_GATEWAY_URL
7
+ authorization_env: PIPELINE_MCP_GATEWAY_AUTHORIZATION
8
+ default_profile: default
9
+ backends:
10
+ context7:
11
+ locality: shared-remote
12
+ tool_prefixes: [context7]
13
+ uidotsh:
14
+ locality: shared-remote
15
+ tool_prefixes: [uidotsh]
16
+ qdrant:
17
+ locality: repo-scoped-remote
18
+ tool_prefixes: [qdrant]
19
+ fallow:
20
+ locality: repo-local
21
+ workspace_path_source: PIPELINE_TARGET_PATH
22
+ required: false
23
+ tool_prefixes: [fallow]
24
+ serena:
25
+ locality: repo-local
26
+ workspace_path_source: PIPELINE_TARGET_PATH
27
+ tool_prefixes: [serena]
28
+ backlog:
29
+ locality: repo-local
30
+ workspace_path_source: PIPELINE_TARGET_PATH
31
+ tool_prefixes: [backlog]
32
+ skills:
33
+ orchestrate:
34
+ path: .agents/skills/orchestrate/SKILL.md
35
+ source_root: package
36
+ execute:
37
+ path: .agents/skills/execute/SKILL.md
38
+ source_root: package
39
+ inspect:
40
+ path: .agents/skills/inspect/SKILL.md
41
+ source_root: package
42
+ quick:
43
+ path: .agents/skills/quick/SKILL.md
44
+ source_root: package
45
+ critique:
46
+ path: .agents/skills/critique/SKILL.md
47
+ source_root: package
48
+ doubt:
49
+ path: .agents/skills/doubt/SKILL.md
50
+ source_root: package
51
+ fix:
52
+ path: .agents/skills/fix/SKILL.md
53
+ source_root: package
54
+ library-first-development:
55
+ path: .agents/skills/library-first-development/SKILL.md
56
+ source_root: package
57
+ migrate:
58
+ path: .agents/skills/migrate/SKILL.md
59
+ source_root: package
60
+ optimize:
61
+ path: .agents/skills/optimize/SKILL.md
62
+ source_root: package
63
+ research:
64
+ path: .agents/skills/research/SKILL.md
65
+ source_root: package
66
+ schedule-graph-shaping:
67
+ path: .agents/skills/schedule-graph-shaping/SKILL.md
68
+ source_root: package
69
+ scope:
70
+ path: .agents/skills/scope/SKILL.md
71
+ source_root: package
72
+ secure:
73
+ path: .agents/skills/secure/SKILL.md
74
+ source_root: package
75
+ spec:
76
+ path: .agents/skills/spec/SKILL.md
77
+ source_root: package
78
+ test:
79
+ path: .agents/skills/test/SKILL.md
80
+ source_root: package
81
+ trace:
82
+ path: .agents/skills/trace/SKILL.md
83
+ source_root: package
84
+ verify:
85
+ path: .agents/skills/verify/SKILL.md
86
+ source_root: package
87
+ profiles:
88
+ moka-orchestrator:
89
+ runner: opencode
90
+ description: Orchestrate the configured pipeline and enforce gates.
91
+ instructions: { inline: "Orchestrate the configured pipeline locally. Load the `orchestrate` skill and spawn the roster as native Task subagents on this machine. Do not submit to Argo or run `moka submit`. Enforce only package-configured gates." }
92
+ skills: [orchestrate, execute, quick, inspect]
93
+ mcp_servers: [pipeline-gateway]
94
+ tools: [read, list, grep, glob, bash]
95
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
96
+ network: { mode: inherit }
97
+ moka-researcher:
98
+ runner: opencode
99
+ description: Research the requested task and produce structured findings.
100
+ instructions: { inline: "Inspect first-party source, tests, docs, and task context for the current task only. Produce concise findings with file references and stop; do not perform open-ended repository exploration." }
101
+ timeout_ms: 900000
102
+ skills: [research, spec, scope]
103
+ mcp_servers: [pipeline-gateway]
104
+ tools: [read, list, grep, glob, bash]
105
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
106
+ network: { mode: inherit }
107
+ output:
108
+ format: json_schema
109
+ schema_path: .pipeline/schemas/research.schema.json
110
+ repair: { enabled: true, max_attempts: 1 }
111
+ moka-inspector:
112
+ runner: opencode
113
+ model: openai/gpt-5.5-low
114
+ description: Inspect the repository without modifying files.
115
+ instructions: { inline: "Inspect the repository without modifying files." }
116
+ skills: [research]
117
+ mcp_servers: [pipeline-gateway]
118
+ tools: [read, list, grep, glob, bash]
119
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
120
+ network: { mode: inherit }
121
+ moka-schedule-planner:
122
+ runner: opencode
123
+ model: openai/gpt-5.5-xhigh
124
+ description: Refine a baseline schedule into a specialized approved-plan artifact.
125
+ instructions: { inline: "Generate exactly one workflow named root as an explicit schedule graph. Return YAML only." }
126
+ timeout_ms: 300000
127
+ skills: [schedule-graph-shaping]
128
+ mcp_servers: [pipeline-gateway]
129
+ tools: [read, list, grep, glob, bash]
130
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
131
+ network: { mode: inherit }
132
+ moka-test-writer:
133
+ runner: opencode
134
+ scheduling_roles: [implementation]
135
+ description: Add focused failing tests for the requested behavior.
136
+ instructions: { inline: "Add focused failing tests for the requested behavior only. Do not change production code. Only edit files matching test paths such as **/*.test.*, **/*.spec.*, **/*_test.*, **/__tests__/**, test/**, or tests/**. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
137
+ skills: [test]
138
+ mcp_servers: [pipeline-gateway]
139
+ tools: [read, list, grep, glob, bash, edit, write]
140
+ filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
141
+ network: { mode: inherit }
142
+ output:
143
+ format: json_schema
144
+ schema_path: .pipeline/schemas/implementation.schema.json
145
+ repair: { enabled: true, max_attempts: 1 }
146
+ moka-code-writer:
147
+ runner: opencode
148
+ scheduling_roles: [implementation]
149
+ description: Implement production code until the failing tests pass.
150
+ instructions: { inline: "Implement the smallest production change that satisfies the failing tests. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
151
+ skills: [trace, test, fix, library-first-development]
152
+ mcp_servers: [pipeline-gateway]
153
+ tools: [read, list, grep, glob, bash, edit, write]
154
+ filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
155
+ network: { mode: inherit }
156
+ output:
157
+ format: json_schema
158
+ schema_path: .pipeline/schemas/implementation.schema.json
159
+ repair: { enabled: true, max_attempts: 1 }
160
+ moka-acceptance-reviewer:
161
+ runner: opencode
162
+ scheduling_roles: [coverage]
163
+ description: Audit the finished change against every acceptance criterion.
164
+ instructions: { inline: 'Audit the completed change against each canonical acceptance criterion independently. Return only valid JSON with top-level "verdict", "evidence", "acceptance", and optional "violations". Each "acceptance" entry must include "id", "verdict", and non-empty "evidence". Do not use Markdown fences or prose outside the JSON object.' }
165
+ skills: [critique, doubt]
166
+ mcp_servers: [pipeline-gateway]
167
+ tools: [read, list, grep, glob, bash]
168
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
169
+ network: { mode: inherit }
170
+ output:
171
+ format: json_schema
172
+ schema_path: .pipeline/schemas/acceptance.schema.json
173
+ repair: { enabled: true, max_attempts: 1 }
174
+ moka-thermo-nuclear-reviewer:
175
+ runner: opencode
176
+ scheduling_roles: [coverage]
177
+ description: Perform the final thermo-nuclear code quality review of the integration branch.
178
+ instructions: { inline: "Perform the final code quality review of the integration branch." }
179
+ skills: [critique]
180
+ mcp_servers: [pipeline-gateway]
181
+ tools: [read, list, grep, glob, bash]
182
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
183
+ network: { mode: inherit }
184
+ output:
185
+ format: json_schema
186
+ schema_path: .pipeline/schemas/review.schema.json
187
+ repair: { enabled: true, max_attempts: 1 }
188
+ moka-verifier:
189
+ runner: opencode
190
+ scheduling_roles: [coverage]
191
+ description: Verify checks, implementation fit, and final evidence.
192
+ instructions: { inline: 'Verify checks, implementation fit, and final evidence. Return only valid JSON with top-level "verdict", "evidence", and optional "violations". Do not use Markdown fences or prose outside the JSON object.' }
193
+ skills: [verify, critique, secure, optimize]
194
+ mcp_servers: [pipeline-gateway]
195
+ tools: [read, list, grep, glob, bash]
196
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
197
+ network: { mode: inherit }
198
+ output:
199
+ format: json_schema
200
+ schema_path: .pipeline/schemas/verify.schema.json
201
+ repair: { enabled: true, max_attempts: 1 }
202
+ moka-learner:
203
+ runner: opencode
204
+ model: openai/gpt-5.5-low
205
+ description: Store durable lessons from the completed run.
206
+ instructions: { inline: "Store durable lessons from the completed run when useful." }
207
+ skills: [migrate]
208
+ mcp_servers: [pipeline-gateway]
209
+ tools: [read, list, grep, glob, bash]
210
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
211
+ network: { mode: inherit }
212
+ output:
213
+ format: json_schema
214
+ schema_path: .pipeline/schemas/learn.schema.json
215
+ repair: { enabled: true, max_attempts: 1 }
@@ -0,0 +1,24 @@
1
+ version: 1
2
+ runners:
3
+ opencode:
4
+ type: opencode
5
+ capabilities:
6
+ native_subagents: true
7
+ rules: true
8
+ skills: true
9
+ mcp_servers: true
10
+ tools: [read, list, grep, glob, bash, edit, write]
11
+ filesystem: [read-only, workspace-write]
12
+ network: [inherit, disabled]
13
+ output_formats: [text, json, jsonl, json_schema]
14
+ command:
15
+ type: command
16
+ capabilities:
17
+ native_subagents: false
18
+ rules: false
19
+ skills: false
20
+ mcp_servers: false
21
+ tools: [bash]
22
+ filesystem: [read-only, workspace-write]
23
+ network: [inherit, disabled]
24
+ output_formats: [text, json]
@@ -1,11 +1,6 @@
1
1
  import { uniqueStrings } from "./strings.js";
2
2
  import { z } from "zod";
3
3
  //#region src/argo-graph.ts
4
- const EXECUTABLE_NODE_KINDS = [
5
- "agent",
6
- "builtin",
7
- "command"
8
- ];
9
4
  const argoExecutableTaskSchema = z.object({
10
5
  dependencies: z.array(z.string().min(1)),
11
6
  nodeId: z.string().min(1),
@@ -18,6 +13,21 @@ const argoExecutionGraphSchema = z.object({
18
13
  terminalTaskNames: z.array(z.string().min(1)),
19
14
  workflowId: z.string().min(1)
20
15
  }).strict();
16
+ /**
17
+ * Thrown when the Argo graph compiler encounters a node kind that cannot be
18
+ * lowered to an Argo DAG task. Callers should surface this as a validation
19
+ * failure before attempting a cluster submission.
20
+ */
21
+ var ArgoGraphCompilerError = class extends Error {
22
+ kind;
23
+ nodeId;
24
+ constructor(kind, nodeId) {
25
+ super(`Argo graph compiler: node kind '${kind}' on node '${nodeId}' cannot be lowered to an Argo DAG task`);
26
+ this.name = "ArgoGraphCompilerError";
27
+ this.kind = kind;
28
+ this.nodeId = nodeId;
29
+ }
30
+ };
21
31
  function compileArgoExecutionGraph(plan) {
22
32
  const compiler = new ArgoGraphCompiler(plan);
23
33
  return argoExecutionGraphSchema.parse(compiler.compile());
@@ -47,41 +57,63 @@ var ArgoGraphCompiler = class {
47
57
  }
48
58
  }
49
59
  compileNodes(nodes, inheritedNeeds) {
50
- for (const node of nodes) {
51
- if (isExecutableNode(node)) {
52
- const dependencies = this.resolveDependencyTaskNames([...inheritedNeeds, ...node.needs]);
53
- const task = argoExecutableTaskSchema.parse({
54
- dependencies,
55
- nodeId: node.id,
56
- taskName: argoTaskName(node.id),
57
- templateName: argoTemplateName(node.id)
58
- });
59
- this.tasks.push(task);
60
- continue;
61
- }
62
- if (node.kind === "group") continue;
63
- if (node.kind === "parallel") this.compileNodes(node.children ?? [], [...inheritedNeeds, ...node.needs]);
60
+ for (const node of nodes) this.compileNode(node, inheritedNeeds);
61
+ }
62
+ compileNode(node, inheritedNeeds) {
63
+ const kind = node.kind;
64
+ switch (kind) {
65
+ case "agent":
66
+ case "builtin":
67
+ case "command":
68
+ this.compileExecutableNode(node, inheritedNeeds);
69
+ return;
70
+ case "group": return;
71
+ case "parallel":
72
+ this.compileParallelNode(node, inheritedNeeds);
73
+ return;
74
+ default: throw new ArgoGraphCompilerError(String(kind), node.id);
64
75
  }
65
76
  }
77
+ compileExecutableNode(node, inheritedNeeds) {
78
+ const dependencies = this.resolveDependencyTaskNames([...inheritedNeeds, ...node.needs]);
79
+ const task = argoExecutableTaskSchema.parse({
80
+ dependencies,
81
+ nodeId: node.id,
82
+ taskName: argoTaskName(node.id),
83
+ templateName: argoTemplateName(node.id)
84
+ });
85
+ this.tasks.push(task);
86
+ }
87
+ compileParallelNode(node, inheritedNeeds) {
88
+ this.compileNodes(node.children ?? [], [...inheritedNeeds, ...node.needs]);
89
+ }
66
90
  resolveDependencyTaskNames(nodeIds) {
67
91
  return uniqueStrings(nodeIds.flatMap((nodeId) => this.resolveDependencyNodeIds(nodeId).map((id) => argoTaskName(id))));
68
92
  }
69
93
  resolveDependencyNodeIds(nodeId) {
70
94
  const node = this.nodeById.get(nodeId);
71
95
  if (!node) return [];
72
- if (isExecutableNode(node)) return [node.id];
73
- if (node.kind === "group") return uniqueStrings([...node.nodes ?? [], ...node.needs].flatMap((id) => this.resolveDependencyNodeIds(id)));
74
- if (node.kind === "parallel") return uniqueStrings((node.children ?? []).flatMap((child) => this.resolveDependencyNodeIds(child.id)));
75
- return [];
96
+ const kind = node.kind;
97
+ switch (kind) {
98
+ case "agent":
99
+ case "builtin":
100
+ case "command": return [node.id];
101
+ case "group": return this.resolveGroupNodeIds(node);
102
+ case "parallel": return this.resolveParallelNodeIds(node);
103
+ default: throw new ArgoGraphCompilerError(String(kind), node.id);
104
+ }
105
+ }
106
+ resolveGroupNodeIds(node) {
107
+ return uniqueStrings([...node.nodes ?? [], ...node.needs].flatMap((id) => this.resolveDependencyNodeIds(id)));
108
+ }
109
+ resolveParallelNodeIds(node) {
110
+ return uniqueStrings((node.children ?? []).flatMap((child) => this.resolveDependencyNodeIds(child.id)));
76
111
  }
77
112
  terminalTasks() {
78
113
  const dependedOn = new Set(this.tasks.flatMap((task) => task.dependencies));
79
114
  return this.tasks.filter((task) => !dependedOn.has(task.taskName));
80
115
  }
81
116
  };
82
- function isExecutableNode(node) {
83
- return EXECUTABLE_NODE_KINDS.includes(node.kind);
84
- }
85
117
  function argoTaskName(nodeId) {
86
118
  return `node-${nodeId}`;
87
119
  }
@@ -89,4 +121,4 @@ function argoTemplateName(nodeId) {
89
121
  return `task-${nodeId}`;
90
122
  }
91
123
  //#endregion
92
- export { compileArgoExecutionGraph };
124
+ export { ArgoGraphCompilerError, compileArgoExecutionGraph };
@@ -22,7 +22,6 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
22
22
  namespace: z.ZodString;
23
23
  opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
24
24
  payloadJson: z.ZodString;
25
- queueName: z.ZodOptional<z.ZodString>;
26
25
  scheduleYaml: z.ZodString;
27
26
  serviceAccountName: z.ZodOptional<z.ZodString>;
28
27
  }, z.core.$strict>;
@@ -1,10 +1,9 @@
1
- import { compileArgoExecutionGraph } from "./argo-graph.js";
1
+ import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
2
2
  import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
3
3
  import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
4
4
  import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
5
+ import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
5
6
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
6
- import { compileScheduleArtifact, parseScheduleArtifact } from "./schedule/planner.js";
7
- import "./schedule-planner.js";
8
7
  import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
9
8
  import { stringify } from "yaml";
10
9
  import { z } from "zod";
@@ -40,7 +39,6 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
40
39
  namespace: z.string().min(1),
41
40
  opencodeAuthSecretName: z.string().min(1).optional(),
42
41
  payloadJson: z.string().min(1),
43
- queueName: z.string().min(1).optional(),
44
42
  scheduleYaml: z.string().min(1),
45
43
  serviceAccountName: z.string().min(1).optional()
46
44
  }).strict().refine((options) => options.name !== void 0 || options.generateName !== void 0, { message: "Argo submit options must declare name or generateName" });
@@ -62,7 +60,7 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
62
60
  const scheduleArtifactConfigMapName = `pipeline-schedule-${randomBytes(6).toString("hex")}`;
63
61
  const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomBytes(6).toString("hex")}`;
64
62
  if (payload.workflow.id !== compiled.workflowId) throw new Error(`Runner payload workflow '${payload.workflow.id}' does not match schedule workflow '${compiled.workflowId}'`);
65
- const graph = compileArgoExecutionGraph(compiled.plan);
63
+ const graph = compileSubmitArgoGraph(compiled);
66
64
  const labels = {
67
65
  "pipeline.oisin.dev/project": payload.run.project,
68
66
  "pipeline.oisin.dev/run-id": payload.run.id,
@@ -89,7 +87,6 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
89
87
  opencodeAuthSecretName: options.opencodeAuthSecretName,
90
88
  payloadConfigMapName,
91
89
  plan: compiled.plan,
92
- queueName: options.queueName,
93
90
  scheduleConfigMapName: scheduleArtifactConfigMapName,
94
91
  serviceAccountName: options.serviceAccountName,
95
92
  taskDescriptorConfigMapName
@@ -184,6 +181,14 @@ function normalizeRunnerPayloadForSubmit(input) {
184
181
  payloadJson: JSON.stringify(payload)
185
182
  };
186
183
  }
184
+ function compileSubmitArgoGraph(compiled) {
185
+ try {
186
+ return compileArgoExecutionGraph(compiled.plan);
187
+ } catch (err) {
188
+ if (err instanceof ArgoGraphCompilerError) throw new Error(`Schedule '${compiled.workflowId}' cannot be submitted: ${err.message}`);
189
+ throw err;
190
+ }
191
+ }
187
192
  function apiClients(options, dependencies) {
188
193
  if (dependencies.coreApi && dependencies.workflowApi) return {
189
194
  coreApi: dependencies.coreApi,
@@ -1,4 +1,4 @@
1
- import { WorkflowExecutionPlan } from "./workflow-planner.js";
1
+ import { WorkflowExecutionPlan } from "./planning/compile.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/argo-workflow.d.ts
@@ -143,7 +143,6 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
143
143
  }, z.core.$strict>>;
144
144
  payloadConfigMapKey: z.ZodDefault<z.ZodString>;
145
145
  payloadConfigMapName: z.ZodString;
146
- queueName: z.ZodOptional<z.ZodString>;
147
146
  resources: z.ZodOptional<z.ZodObject<{
148
147
  limits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
149
148
  requests: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -153,7 +153,6 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
153
153
  }).strict().optional(),
154
154
  payloadConfigMapKey: z.string().min(1).default("payload.json"),
155
155
  payloadConfigMapName: kubernetesNameSchema,
156
- queueName: kubernetesNameSchema.optional(),
157
156
  resources: argoWorkflowResourceRequirementsSchema.optional(),
158
157
  scheduleConfigMapKey: z.string().min(1).default("schedule.yaml"),
159
158
  scheduleConfigMapName: kubernetesNameSchema,
@@ -191,7 +190,6 @@ function buildRunnerArgoWorkflowManifest(rawOptions) {
191
190
  ...options.activeDeadlineSeconds ? { activeDeadlineSeconds: options.activeDeadlineSeconds } : {},
192
191
  entrypoint: RUNNER_WORKFLOW_ENTRYPOINT,
193
192
  ...options.imagePullSecretName ? { imagePullSecrets: [{ name: options.imagePullSecretName }] } : {},
194
- ...options.queueName ? { podMetadata: { labels: { "kueue.x-k8s.io/queue-name": options.queueName } } } : {},
195
193
  onExit: "pipeline-finalizer",
196
194
  serviceAccountName: options.serviceAccountName,
197
195
  templates: [