@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.
- package/.agents/skills/orchestrate/SKILL.md +80 -0
- package/defaults/pipeline.yaml +111 -0
- package/defaults/profiles.yaml +215 -0
- package/defaults/runners.yaml +24 -0
- package/dist/argo-graph.js +59 -27
- package/dist/argo-submit.d.ts +0 -1
- package/dist/argo-submit.js +11 -6
- package/dist/argo-workflow.d.ts +1 -2
- package/dist/argo-workflow.js +0 -2
- package/dist/claude-settings-config.js +44 -0
- package/dist/cli/program.js +12 -5
- package/dist/cli/submit-options.js +1 -2
- package/dist/cluster-doctor.js +0 -12
- package/dist/commands/pipeline-command.js +1 -1
- package/dist/config/defaults.js +7 -350
- package/dist/config/schemas.d.ts +9 -9
- package/dist/install-commands/claude-code.js +160 -0
- package/dist/install-commands/opencode.js +56 -39
- package/dist/install-commands/shared.js +32 -5
- package/dist/install-commands.js +26 -15
- package/dist/json-config-merge.js +47 -0
- package/dist/mcp/gateway.js +9 -1
- package/dist/moka-global-config.d.ts +0 -1
- package/dist/moka-global-config.js +0 -1
- package/dist/moka-submit.d.ts +8 -11
- package/dist/moka-submit.js +1 -4
- package/dist/opencode-project-config.js +2 -45
- package/dist/pipeline-runtime.d.ts +9 -0
- package/dist/pipeline-runtime.js +50 -8
- package/dist/planned-node.js +2 -5
- package/dist/{workflow-planner.d.ts → planning/compile.d.ts} +2 -2
- package/dist/{workflow-planner.js → planning/compile.js} +6 -83
- package/dist/{schedule/planner.d.ts → planning/generate.d.ts} +17 -3
- package/dist/{schedule/planner.js → planning/generate.js} +24 -56
- package/dist/planning/graph.js +138 -0
- package/dist/runner-command/lifecycle-context.js +2 -3
- package/dist/runner-command/run.js +2 -3
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner-event-schema.d.ts +349 -0
- package/dist/runner-event-schema.js +185 -0
- package/dist/runner-output.js +2 -2
- package/dist/runner.d.ts +29 -0
- package/dist/runtime/agent-node/agent-node.js +1 -0
- package/dist/runtime/context/context.js +1 -1
- package/dist/runtime/contracts/contracts.d.ts +18 -1
- package/dist/runtime/node-state-store.js +7 -0
- package/dist/runtime/opencode-adapter.js +28 -8
- package/dist/runtime/opencode-agent-name.js +18 -0
- package/dist/runtime/opencode-runtime.js +62 -0
- package/dist/runtime/opencode-server.js +67 -0
- package/dist/runtime/opencode-session-executor.js +206 -0
- package/dist/schedule/passes/coverage.js +7 -51
- package/dist/schedule/passes/ids.js +3 -23
- package/dist/schedule/scheduling-roles.js +19 -0
- package/dist/strings.js +30 -1
- package/docs/adr-opencode-first-goal-loop-runtime.md +1 -1
- package/docs/config-architecture.md +43 -6
- package/docs/mcp-gateway.md +4 -4
- package/docs/operator-guide.md +9 -8
- package/docs/pipeline-console-runner-contract.md +3 -4
- package/docs/slash-command-adapter-contract.md +1 -0
- package/package.json +10 -5
- package/dist/schedule-planner.d.ts +0 -2
- 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]
|
package/dist/argo-graph.js
CHANGED
|
@@ -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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 };
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -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>;
|
package/dist/argo-submit.js
CHANGED
|
@@ -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 =
|
|
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,
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WorkflowExecutionPlan } from "./
|
|
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>>;
|
package/dist/argo-workflow.js
CHANGED
|
@@ -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: [
|