stageflow 0.16.0 → 0.18.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/README.md +2 -4
- package/dist/agent/claudeTools.d.ts +1 -4
- package/dist/agent/claudeTools.js +1 -6
- package/dist/agent/fakeAgent.js +4 -5
- package/dist/agent/piAdapter.js +1 -14
- package/dist/config/cloneChain.d.ts +12 -0
- package/dist/config/cloneChain.js +322 -0
- package/dist/config/createPipeline.js +101 -21
- package/dist/config/createStage.js +7 -0
- package/dist/config/loadOutcome.d.ts +1 -0
- package/dist/config/loadPipeline.d.ts +2 -0
- package/dist/config/loadPipeline.js +34 -22
- package/dist/config/loadStage.d.ts +2 -0
- package/dist/config/loadStage.js +135 -125
- package/dist/config/migrateYaml.js +14 -3
- package/dist/config/normalizePipelineStageEntry.d.ts +5 -9
- package/dist/config/normalizePipelineStageEntry.js +133 -60
- package/dist/config/parsePreEmitChecks.js +2 -2
- package/dist/config/pipelineNeeds.d.ts +15 -2
- package/dist/config/pipelineNeeds.js +86 -4
- package/dist/config/pipelineRoute.d.ts +46 -0
- package/dist/config/pipelineRoute.js +215 -0
- package/dist/config/pipelineStageKeys.d.ts +2 -0
- package/dist/config/pipelineStageKeys.js +15 -4
- package/dist/config/printTargetYaml.js +18 -12
- package/dist/config/resolveForkEmitContext.d.ts +1 -1
- package/dist/config/resolveForkEmitContext.js +3 -26
- package/dist/config/resolvePipelineDag.d.ts +1 -3
- package/dist/config/resolvePipelineDag.js +143 -186
- package/dist/config/routeIf.d.ts +24 -0
- package/dist/config/routeIf.js +247 -0
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +5 -3
- package/dist/config/yamlDialect.d.ts +5 -8
- package/dist/config/yamlDialect.js +37 -29
- package/dist/envelope/check.d.ts +0 -2
- package/dist/envelope/check.js +1 -57
- package/dist/envelope/feedbackLoop.js +0 -3
- package/dist/envelope/forkChoice.js +1 -1
- package/dist/envelope/payloadSchema.d.ts +14 -0
- package/dist/envelope/payloadSchema.js +87 -2
- package/dist/mcp/catalogTools.js +1 -2
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/projection/projectRun.d.ts +0 -2
- package/dist/projection/projectRun.js +0 -3
- package/dist/prompt/priorEnvelope.js +1 -1
- package/dist/runstore/stageInstanceId.d.ts +1 -0
- package/dist/runstore/stageInstanceId.js +9 -0
- package/dist/runtime/cloneSchedule.d.ts +16 -33
- package/dist/runtime/cloneSchedule.js +155 -236
- package/dist/runtime/envelopeRouting.js +43 -94
- package/dist/runtime/forkGeneration.d.ts +2 -2
- package/dist/runtime/forkGeneration.js +29 -15
- package/dist/runtime/joinReadiness.d.ts +16 -0
- package/dist/runtime/joinReadiness.js +156 -0
- package/dist/runtime/pipelineScheduler.d.ts +0 -3
- package/dist/runtime/pipelineScheduler.js +81 -65
- package/dist/runtime/routeIfEval.d.ts +3 -0
- package/dist/runtime/routeIfEval.js +94 -0
- package/dist/runtime/runRetryCoordinator.js +0 -4
- package/dist/runtime/stageAttemptBootstrap.js +60 -11
- package/dist/runtime/taskInput.js +1 -13
- package/dist/tools/emitStageEnvelope.d.ts +2 -3
- package/dist/tools/emitStageEnvelope.js +1 -65
- package/dist/types/envelope.d.ts +0 -2
- package/dist/types/forkChoice.d.ts +0 -16
- package/dist/types/pipeline.d.ts +86 -27
- package/dist/types/stage.d.ts +11 -7
- package/dist/ui/assets/{index-D2Wt8hwH.css → index-DsgVjLUS.css} +1 -1
- package/dist/ui/assets/{index-XWJovnp7.js → index-DwKGaQvQ.js} +13 -12
- package/dist/ui/index.html +2 -2
- package/package.json +2 -1
- package/skills/stageflow-author/SKILL.md +4 -4
- package/skills/stageflow-author/assets/examples/branch-decision/hotfix.yaml +10 -2
- package/skills/stageflow-author/assets/examples/branch-decision/release-gate.pipeline.yaml +12 -4
- package/skills/stageflow-author/assets/examples/branch-decision/run-tests.yaml +6 -4
- package/skills/stageflow-author/assets/examples/branch-decision/ship.yaml +9 -1
- package/skills/stageflow-author/assets/examples/linear-review/draft.yaml +3 -0
- package/skills/stageflow-author/assets/examples/linear-review/publish.yaml +8 -0
- package/skills/stageflow-author/assets/examples/linear-review/review-loop.pipeline.yaml +5 -2
- package/skills/stageflow-author/assets/examples/linear-review/review.yaml +8 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/gather.yaml +3 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/research-digest.pipeline.yaml +5 -2
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/send.yaml +8 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/summarize.yaml +8 -0
- package/skills/stageflow-author/references/catalog-mapping.md +159 -50
- package/skills/stageflow-author/references/catalog-write-conventions.md +3 -3
- package/skills/stageflow-author/references/stage-prompt-template.md +57 -17
- package/skills/stageflow-session-capture/SKILL.md +2 -2
- package/skills/stageflow-session-capture/assets/example-pipeline/example.pipeline.yaml +3 -1
- package/skills/stageflow-session-capture/assets/example-pipeline/implement.yaml +15 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/research.yaml +3 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +4 -2
- package/dist/envelope/cloneForks.d.ts +0 -2
- package/dist/envelope/cloneForks.js +0 -77
|
@@ -6,8 +6,10 @@ export declare function resolvePipelinePath(pipelinePath: string, cwd: string):
|
|
|
6
6
|
export declare function loadPipelineOutcome(nameOrPath: string, options?: {
|
|
7
7
|
cwd?: string;
|
|
8
8
|
projectRoot?: string;
|
|
9
|
+
requireIo?: boolean;
|
|
9
10
|
}): Promise<LoadOutcome<LoadedPipeline>>;
|
|
10
11
|
export declare function loadPipeline(nameOrPath: string, options?: {
|
|
11
12
|
cwd?: string;
|
|
12
13
|
projectRoot?: string;
|
|
14
|
+
requireIo?: boolean;
|
|
13
15
|
}): Promise<LoadedPipeline>;
|
|
@@ -10,6 +10,8 @@ import { predecessorEdges } from "./pipelineNeeds.js";
|
|
|
10
10
|
import { resolvePipelineDagFromRefs } from "./resolvePipelineDag.js";
|
|
11
11
|
import { recoveryRequiresCompletionIssue } from "./parseCompletionContract.js";
|
|
12
12
|
import { validateCompletionContractForStage } from "./validateCompletionContract.js";
|
|
13
|
+
import { applyCloneChains } from "./cloneChain.js";
|
|
14
|
+
import { collectRouteIfSchemaIssues, collectRouteIfIllegalCombos, collectRouteAllGatedWarnings, } from "./routeIf.js";
|
|
13
15
|
function schemaCompileIssue(stageId, field, err, pipelineId) {
|
|
14
16
|
const message = err instanceof Error ? err.message : String(err);
|
|
15
17
|
if (err instanceof UnresolvedSchemaRefError) {
|
|
@@ -59,26 +61,23 @@ function checkSequentialIoCompatibility(stages, dag, pipelineId, schemas) {
|
|
|
59
61
|
const node = nodeById.get(child.id);
|
|
60
62
|
if (!node)
|
|
61
63
|
continue;
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
pipelineId,
|
|
80
|
-
},
|
|
81
|
-
]);
|
|
64
|
+
for (const parentEdge of predecessorEdges(node)) {
|
|
65
|
+
const parentNode = nodeById.get(parentEdge.id);
|
|
66
|
+
if (parentNode?.clone_array_field !== undefined)
|
|
67
|
+
continue;
|
|
68
|
+
const parent = stageById.get(parentEdge.id);
|
|
69
|
+
if (!parent?.payload_schema || child.clone_input_schema === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
if (!isPayloadSchemaSubset(child.clone_input_schema, parent.payload_schema, options)) {
|
|
72
|
+
return loadFailure([
|
|
73
|
+
{
|
|
74
|
+
code: "pipeline.io_incompatible",
|
|
75
|
+
message: `Pipeline ${pipelineId}: stage "${child.id}" io.input is not a structural subset of "${parent.id}" io.output`,
|
|
76
|
+
category: "pipeline",
|
|
77
|
+
pipelineId,
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
return loadSuccess(undefined);
|
|
@@ -93,7 +92,7 @@ export async function resolvePipelinePath(pipelinePath, cwd) {
|
|
|
93
92
|
throw new Error(`Pipeline file not found: ${resolved}. Pass a filesystem path to --pipeline (e.g. pipelines/hello.pipeline.yaml), not a bare pipeline id.`);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
|
-
async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd) {
|
|
95
|
+
async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd, requireIo = true) {
|
|
97
96
|
const normalizedPipelinePath = path.normalize(path.resolve(pipelinePath));
|
|
98
97
|
const mergeOutcome = await mergePipelineStages(normalizedPipelinePath);
|
|
99
98
|
if (!mergeOutcome.ok) {
|
|
@@ -147,6 +146,7 @@ async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd) {
|
|
|
147
146
|
entryId: entry.id,
|
|
148
147
|
declaringPath: entry.declaringPath,
|
|
149
148
|
deferSchemaRefs: true,
|
|
149
|
+
requireIo,
|
|
150
150
|
});
|
|
151
151
|
if (!inlineOutcome.ok) {
|
|
152
152
|
return loadFailure(inlineOutcome.issues);
|
|
@@ -157,6 +157,7 @@ async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd) {
|
|
|
157
157
|
}
|
|
158
158
|
const stageOutcome = await loadStageOutcome(entry.body.absolutePath, {
|
|
159
159
|
deferSchemaRefs: true,
|
|
160
|
+
requireIo,
|
|
160
161
|
});
|
|
161
162
|
if (!stageOutcome.ok) {
|
|
162
163
|
if (stageOutcome.issues.some((issue) => issue.code === "catalog.mixed_yaml_dialect")) {
|
|
@@ -206,9 +207,20 @@ async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd) {
|
|
|
206
207
|
stages.push(stage);
|
|
207
208
|
stageSources[stageId] = { kind: "file", path: entry.body.absolutePath };
|
|
208
209
|
}
|
|
210
|
+
const cloneChainOutcome = applyCloneChains(stages, wiringRefs, dag, pipelineId);
|
|
211
|
+
if (!cloneChainOutcome.ok)
|
|
212
|
+
return cloneChainOutcome;
|
|
209
213
|
const schemaOutcome = attachPipelineSchemas(stages, pipelineSchemas, pipelineId);
|
|
210
214
|
if (!schemaOutcome.ok)
|
|
211
215
|
return schemaOutcome;
|
|
216
|
+
const routeIfIssues = [
|
|
217
|
+
...collectRouteIfSchemaIssues(stages, wiringRefs, pipelineId),
|
|
218
|
+
...collectRouteIfIllegalCombos(wiringRefs, pipelineId),
|
|
219
|
+
];
|
|
220
|
+
if (routeIfIssues.length > 0) {
|
|
221
|
+
return loadFailure(routeIfIssues);
|
|
222
|
+
}
|
|
223
|
+
warnings.push(...collectRouteAllGatedWarnings(wiringRefs, pipelineId));
|
|
212
224
|
const ioOutcome = checkSequentialIoCompatibility(stages, dag, pipelineId, pipelineSchemas);
|
|
213
225
|
if (!ioOutcome.ok)
|
|
214
226
|
return ioOutcome;
|
|
@@ -283,7 +295,7 @@ export async function loadPipelineOutcome(nameOrPath, options = {}) {
|
|
|
283
295
|
},
|
|
284
296
|
]);
|
|
285
297
|
}
|
|
286
|
-
return loadPipelineFromPath(pipelinePath, cwd, projectRoot);
|
|
298
|
+
return loadPipelineFromPath(pipelinePath, cwd, projectRoot, options.requireIo !== false);
|
|
287
299
|
}
|
|
288
300
|
export async function loadPipeline(nameOrPath, options = {}) {
|
|
289
301
|
const outcome = await loadPipelineOutcome(nameOrPath, options);
|
|
@@ -6,11 +6,13 @@ export declare function afterCompletionForStage(stage: StageConfig): CompletionC
|
|
|
6
6
|
export declare function parseStageMcp(raw: unknown, label: string, stageId?: string): LoadOutcome<string[] | undefined>;
|
|
7
7
|
export type LoadStageOptions = {
|
|
8
8
|
deferSchemaRefs?: boolean;
|
|
9
|
+
requireIo?: boolean;
|
|
9
10
|
};
|
|
10
11
|
export declare function loadStageFromObjectOutcome(raw: Record<string, unknown>, ctx: {
|
|
11
12
|
entryId: string;
|
|
12
13
|
declaringPath: string;
|
|
13
14
|
deferSchemaRefs?: boolean;
|
|
15
|
+
requireIo?: boolean;
|
|
14
16
|
}): LoadOutcome<StageConfig>;
|
|
15
17
|
export declare function loadStageOutcome(filePath: string, options?: LoadStageOptions): Promise<LoadOutcome<StageConfig>>;
|
|
16
18
|
export declare function loadStage(filePath: string): Promise<StageConfig>;
|
package/dist/config/loadStage.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { parseAgentField } from "../agent/agentBackend.js";
|
|
2
2
|
import { STAGEFLOW_MCP_SERVER_NAME } from "../agent/claudeTools.js";
|
|
3
|
-
import { CLONE_ACTIONS } from "../types/forkChoice.js";
|
|
4
3
|
import { STAGE_GATE_KINDS, } from "../types/stage.js";
|
|
5
4
|
import { compilePayloadSchema, UnresolvedSchemaRefError } from "../envelope/payloadSchema.js";
|
|
6
5
|
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
@@ -8,7 +7,7 @@ import { parseModelField } from "./modelField.js";
|
|
|
8
7
|
import { allowLegacyYamlAuthoring, dialectWarningForDocument, legacyAuthoringRejected, presentLegacyKeys, } from "./legacyYaml.js";
|
|
9
8
|
import { parsePreEmitChecks } from "./parsePreEmitChecks.js";
|
|
10
9
|
import { readYamlObject } from "./readYamlObject.js";
|
|
11
|
-
import {
|
|
10
|
+
import { classifyYamlDocument, compileTargetContract, dialectFromKeys, mixedDialectIssue, STAGE_FILE_WIRING_KEYS, } from "./yamlDialect.js";
|
|
12
11
|
const afterCompletionByStage = new WeakMap();
|
|
13
12
|
/** After-phase IR from target `verify` (`when` includes after). Stamped onto DAG `completion`. */
|
|
14
13
|
export function afterCompletionForStage(stage) {
|
|
@@ -17,45 +16,6 @@ export function afterCompletionForStage(stage) {
|
|
|
17
16
|
function isGateKind(value) {
|
|
18
17
|
return STAGE_GATE_KINDS.includes(value);
|
|
19
18
|
}
|
|
20
|
-
function isCloneAction(value) {
|
|
21
|
-
return CLONE_ACTIONS.includes(value);
|
|
22
|
-
}
|
|
23
|
-
function parseCloneActions(raw, label) {
|
|
24
|
-
if (raw === undefined)
|
|
25
|
-
return loadSuccess(undefined);
|
|
26
|
-
if (!Array.isArray(raw) || !raw.every((item) => typeof item === "string")) {
|
|
27
|
-
return loadFailure([
|
|
28
|
-
{
|
|
29
|
-
code: "stage.invalid_clone_actions",
|
|
30
|
-
message: `Invalid stage ${label}: clone_actions must be a non-empty array of strings`,
|
|
31
|
-
category: "stage",
|
|
32
|
-
},
|
|
33
|
-
]);
|
|
34
|
-
}
|
|
35
|
-
if (raw.length === 0) {
|
|
36
|
-
return loadFailure([
|
|
37
|
-
{
|
|
38
|
-
code: "stage.invalid_clone_actions",
|
|
39
|
-
message: `Invalid stage ${label}: clone_actions must not be empty`,
|
|
40
|
-
category: "stage",
|
|
41
|
-
},
|
|
42
|
-
]);
|
|
43
|
-
}
|
|
44
|
-
const actions = [];
|
|
45
|
-
for (const item of raw) {
|
|
46
|
-
if (!isCloneAction(item)) {
|
|
47
|
-
return loadFailure([
|
|
48
|
-
{
|
|
49
|
-
code: "stage.invalid_clone_actions",
|
|
50
|
-
message: `Invalid stage ${label}: unsupported clone_actions value "${item}" (allowed: ${CLONE_ACTIONS.join(", ")})`,
|
|
51
|
-
category: "stage",
|
|
52
|
-
},
|
|
53
|
-
]);
|
|
54
|
-
}
|
|
55
|
-
actions.push(item);
|
|
56
|
-
}
|
|
57
|
-
return loadSuccess(actions);
|
|
58
|
-
}
|
|
59
19
|
function parseTimeoutMs(raw, label) {
|
|
60
20
|
if (raw === undefined)
|
|
61
21
|
return loadSuccess(undefined);
|
|
@@ -162,9 +122,7 @@ export function parseStageMcp(raw, label, stageId) {
|
|
|
162
122
|
}
|
|
163
123
|
return loadSuccess(names);
|
|
164
124
|
}
|
|
165
|
-
function parseStageFields(raw, label, entryId, deferSchemaRefs) {
|
|
166
|
-
// Reads IR field names. Target YAML must already be compiled via
|
|
167
|
-
// applyCompiledBody; legacy YAML uses these keys as authoring.
|
|
125
|
+
function parseStageFields(raw, label, entryId, deferSchemaRefs, requireIo, compiled) {
|
|
168
126
|
if (typeof raw.system_prompt !== "string") {
|
|
169
127
|
return loadFailure([
|
|
170
128
|
{
|
|
@@ -179,6 +137,16 @@ function parseStageFields(raw, label, entryId, deferSchemaRefs) {
|
|
|
179
137
|
id: entryId,
|
|
180
138
|
system_prompt: raw.system_prompt,
|
|
181
139
|
};
|
|
140
|
+
if (raw.clone_actions !== undefined) {
|
|
141
|
+
return loadFailure([
|
|
142
|
+
{
|
|
143
|
+
code: "stage.invalid_clone_actions",
|
|
144
|
+
message: `Invalid stage ${label}: "clone_actions" is no longer supported — use a Clone Chain instead`,
|
|
145
|
+
category: "stage",
|
|
146
|
+
stageId: entryId,
|
|
147
|
+
},
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
182
150
|
const modelField = parseModelField(raw.model);
|
|
183
151
|
if (!modelField.ok) {
|
|
184
152
|
return loadFailure([
|
|
@@ -193,102 +161,133 @@ function parseStageFields(raw, label, entryId, deferSchemaRefs) {
|
|
|
193
161
|
if (modelField.value !== undefined) {
|
|
194
162
|
stage.model = modelField.value;
|
|
195
163
|
}
|
|
196
|
-
if (
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
Array.isArray(raw.payload_schema)) {
|
|
200
|
-
return loadFailure([
|
|
201
|
-
{
|
|
202
|
-
code: "stage.invalid_payload_schema",
|
|
203
|
-
message: `Invalid stage ${label}: payload_schema must be an object`,
|
|
204
|
-
category: "stage",
|
|
205
|
-
stageId: entryId,
|
|
206
|
-
},
|
|
207
|
-
]);
|
|
164
|
+
if (compiled) {
|
|
165
|
+
if (compiled.payload_schema !== undefined) {
|
|
166
|
+
stage.payload_schema = compiled.payload_schema;
|
|
208
167
|
}
|
|
209
|
-
|
|
210
|
-
|
|
168
|
+
if (compiled.clone_input_schema !== undefined) {
|
|
169
|
+
stage.clone_input_schema = compiled.clone_input_schema;
|
|
211
170
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
if (raw.payload_schema !== undefined) {
|
|
174
|
+
if (raw.payload_schema === null ||
|
|
175
|
+
typeof raw.payload_schema !== "object" ||
|
|
176
|
+
Array.isArray(raw.payload_schema)) {
|
|
177
|
+
return loadFailure([
|
|
178
|
+
{
|
|
179
|
+
code: "stage.invalid_payload_schema",
|
|
180
|
+
message: `Invalid stage ${label}: io.output.schema must be an object`,
|
|
181
|
+
category: "stage",
|
|
182
|
+
stageId: entryId,
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
compilePayloadSchema(raw.payload_schema);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
if (err instanceof UnresolvedSchemaRefError) {
|
|
191
|
+
if (!deferSchemaRefs) {
|
|
192
|
+
return loadFailure([
|
|
193
|
+
{
|
|
194
|
+
code: "stage.unresolved_schema_ref",
|
|
195
|
+
message: `Invalid stage ${label}: ${err.message}`,
|
|
196
|
+
category: "stage",
|
|
197
|
+
stageId: entryId,
|
|
198
|
+
},
|
|
199
|
+
]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
215
204
|
return loadFailure([
|
|
216
205
|
{
|
|
217
|
-
code: "stage.
|
|
218
|
-
message: `Invalid stage ${label}: ${
|
|
206
|
+
code: "stage.invalid_payload_schema",
|
|
207
|
+
message: `Invalid stage ${label}: invalid io.output.schema: ${message}`,
|
|
219
208
|
category: "stage",
|
|
220
209
|
stageId: entryId,
|
|
221
210
|
},
|
|
222
211
|
]);
|
|
223
212
|
}
|
|
224
213
|
}
|
|
225
|
-
|
|
226
|
-
|
|
214
|
+
stage.payload_schema = raw.payload_schema;
|
|
215
|
+
}
|
|
216
|
+
if (raw.clone_input_schema !== undefined) {
|
|
217
|
+
if (raw.clone_input_schema === null ||
|
|
218
|
+
typeof raw.clone_input_schema !== "object" ||
|
|
219
|
+
Array.isArray(raw.clone_input_schema)) {
|
|
227
220
|
return loadFailure([
|
|
228
221
|
{
|
|
229
|
-
code: "stage.
|
|
230
|
-
message: `Invalid stage ${label}:
|
|
222
|
+
code: "stage.invalid_clone_input_schema",
|
|
223
|
+
message: `Invalid stage ${label}: io.input.schema must be an object`,
|
|
231
224
|
category: "stage",
|
|
232
225
|
stageId: entryId,
|
|
233
226
|
},
|
|
234
227
|
]);
|
|
235
228
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
catch (err) {
|
|
256
|
-
if (err instanceof UnresolvedSchemaRefError) {
|
|
257
|
-
if (!deferSchemaRefs) {
|
|
229
|
+
try {
|
|
230
|
+
compilePayloadSchema(raw.clone_input_schema);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
if (err instanceof UnresolvedSchemaRefError) {
|
|
234
|
+
if (!deferSchemaRefs) {
|
|
235
|
+
return loadFailure([
|
|
236
|
+
{
|
|
237
|
+
code: "stage.unresolved_schema_ref",
|
|
238
|
+
message: `Invalid stage ${label}: ${err.message}`,
|
|
239
|
+
category: "stage",
|
|
240
|
+
stageId: entryId,
|
|
241
|
+
},
|
|
242
|
+
]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
258
247
|
return loadFailure([
|
|
259
248
|
{
|
|
260
|
-
code: "stage.
|
|
261
|
-
message: `Invalid stage ${label}: ${
|
|
249
|
+
code: "stage.invalid_clone_input_schema",
|
|
250
|
+
message: `Invalid stage ${label}: invalid io.input.schema: ${message}`,
|
|
262
251
|
category: "stage",
|
|
263
252
|
stageId: entryId,
|
|
264
253
|
},
|
|
265
254
|
]);
|
|
266
255
|
}
|
|
267
256
|
}
|
|
268
|
-
|
|
269
|
-
|
|
257
|
+
stage.clone_input_schema = raw.clone_input_schema;
|
|
258
|
+
}
|
|
259
|
+
if (requireIo) {
|
|
260
|
+
if (raw.payload_schema === undefined && raw.clone_input_schema === undefined) {
|
|
270
261
|
return loadFailure([
|
|
271
262
|
{
|
|
272
|
-
code: "stage.
|
|
273
|
-
message: `Invalid stage ${label}:
|
|
263
|
+
code: "stage.invalid_io",
|
|
264
|
+
message: `Invalid stage ${label}: io is required`,
|
|
265
|
+
category: "stage",
|
|
266
|
+
stageId: entryId,
|
|
267
|
+
},
|
|
268
|
+
]);
|
|
269
|
+
}
|
|
270
|
+
if (raw.payload_schema === undefined) {
|
|
271
|
+
return loadFailure([
|
|
272
|
+
{
|
|
273
|
+
code: "stage.invalid_io",
|
|
274
|
+
message: `Invalid stage ${label}: io.output.schema is required`,
|
|
275
|
+
category: "stage",
|
|
276
|
+
stageId: entryId,
|
|
277
|
+
},
|
|
278
|
+
]);
|
|
279
|
+
}
|
|
280
|
+
if (raw.clone_input_schema === undefined) {
|
|
281
|
+
return loadFailure([
|
|
282
|
+
{
|
|
283
|
+
code: "stage.invalid_io",
|
|
284
|
+
message: `Invalid stage ${label}: io.input.schema is required`,
|
|
274
285
|
category: "stage",
|
|
275
286
|
stageId: entryId,
|
|
276
287
|
},
|
|
277
288
|
]);
|
|
278
289
|
}
|
|
279
290
|
}
|
|
280
|
-
stage.clone_input_schema = raw.clone_input_schema;
|
|
281
|
-
}
|
|
282
|
-
const cloneActionsOutcome = parseCloneActions(raw.clone_actions, label);
|
|
283
|
-
if (!cloneActionsOutcome.ok) {
|
|
284
|
-
const issues = cloneActionsOutcome.issues.map((issue) => ({
|
|
285
|
-
...issue,
|
|
286
|
-
stageId: entryId,
|
|
287
|
-
}));
|
|
288
|
-
return loadFailure(issues);
|
|
289
|
-
}
|
|
290
|
-
if (cloneActionsOutcome.value !== undefined) {
|
|
291
|
-
stage.clone_actions = cloneActionsOutcome.value;
|
|
292
291
|
}
|
|
293
292
|
const gateKindsOutcome = parseGateKinds(raw.gate_kinds, label);
|
|
294
293
|
if (!gateKindsOutcome.ok) {
|
|
@@ -312,16 +311,23 @@ function parseStageFields(raw, label, entryId, deferSchemaRefs) {
|
|
|
312
311
|
if (timeoutMsOutcome.value !== undefined) {
|
|
313
312
|
stage.timeout_ms = timeoutMsOutcome.value;
|
|
314
313
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
stageId: entryId,
|
|
320
|
-
}));
|
|
321
|
-
return loadFailure(issues);
|
|
314
|
+
if (compiled) {
|
|
315
|
+
if (compiled.pre_emit_checks !== undefined) {
|
|
316
|
+
stage.pre_emit_checks = compiled.pre_emit_checks;
|
|
317
|
+
}
|
|
322
318
|
}
|
|
323
|
-
|
|
324
|
-
|
|
319
|
+
else {
|
|
320
|
+
const preEmitChecksOutcome = parsePreEmitChecks(raw.pre_emit_checks, label);
|
|
321
|
+
if (!preEmitChecksOutcome.ok) {
|
|
322
|
+
const issues = preEmitChecksOutcome.issues.map((issue) => ({
|
|
323
|
+
...issue,
|
|
324
|
+
stageId: entryId,
|
|
325
|
+
}));
|
|
326
|
+
return loadFailure(issues);
|
|
327
|
+
}
|
|
328
|
+
if (preEmitChecksOutcome.value !== undefined) {
|
|
329
|
+
stage.pre_emit_checks = preEmitChecksOutcome.value;
|
|
330
|
+
}
|
|
325
331
|
}
|
|
326
332
|
if (raw.skill !== undefined) {
|
|
327
333
|
if (typeof raw.skill !== "string" || raw.skill.trim() === "") {
|
|
@@ -361,6 +367,7 @@ function parseStageFields(raw, label, entryId, deferSchemaRefs) {
|
|
|
361
367
|
export function loadStageFromObjectOutcome(raw, ctx) {
|
|
362
368
|
const label = `${ctx.entryId} (${ctx.declaringPath})`;
|
|
363
369
|
const deferSchemaRefs = ctx.deferSchemaRefs === true;
|
|
370
|
+
const requireIo = ctx.requireIo !== false;
|
|
364
371
|
const dialect = dialectFromKeys(Object.keys(raw));
|
|
365
372
|
if (dialect === "invalid") {
|
|
366
373
|
return loadFailure([mixedDialectIssue()]);
|
|
@@ -374,12 +381,13 @@ export function loadStageFromObjectOutcome(raw, ctx) {
|
|
|
374
381
|
label,
|
|
375
382
|
category: "stage",
|
|
376
383
|
deferSchemaRefs,
|
|
384
|
+
requireIo,
|
|
377
385
|
});
|
|
378
386
|
if (!compiled.ok)
|
|
379
387
|
return compiled;
|
|
380
|
-
return parseStageFields(
|
|
388
|
+
return parseStageFields(raw, label, ctx.entryId, deferSchemaRefs, requireIo, compiled.value);
|
|
381
389
|
}
|
|
382
|
-
return parseStageFields(raw, label, ctx.entryId, deferSchemaRefs);
|
|
390
|
+
return parseStageFields(raw, label, ctx.entryId, deferSchemaRefs, requireIo);
|
|
383
391
|
}
|
|
384
392
|
export async function loadStageOutcome(filePath, options = {}) {
|
|
385
393
|
let raw;
|
|
@@ -425,22 +433,24 @@ export async function loadStageOutcome(filePath, options = {}) {
|
|
|
425
433
|
]);
|
|
426
434
|
}
|
|
427
435
|
}
|
|
428
|
-
let
|
|
436
|
+
let compiled;
|
|
429
437
|
let afterCompletion;
|
|
430
438
|
const deferSchemaRefs = options.deferSchemaRefs === true;
|
|
439
|
+
const requireIo = options.requireIo !== false;
|
|
431
440
|
if (dialect === "target") {
|
|
432
|
-
const
|
|
441
|
+
const compiledOutcome = compileTargetContract(raw, {
|
|
433
442
|
stageId: raw.id,
|
|
434
443
|
label: `file ${filePath}`,
|
|
435
444
|
category: "stage",
|
|
436
445
|
deferSchemaRefs,
|
|
446
|
+
requireIo,
|
|
437
447
|
});
|
|
438
|
-
if (!
|
|
439
|
-
return
|
|
440
|
-
|
|
441
|
-
afterCompletion = compiled.
|
|
448
|
+
if (!compiledOutcome.ok)
|
|
449
|
+
return compiledOutcome;
|
|
450
|
+
compiled = compiledOutcome.value;
|
|
451
|
+
afterCompletion = compiled.completion;
|
|
442
452
|
}
|
|
443
|
-
const outcome = parseStageFields(
|
|
453
|
+
const outcome = parseStageFields(raw, `file ${filePath}`, raw.id, deferSchemaRefs, requireIo, compiled);
|
|
444
454
|
if (!outcome.ok)
|
|
445
455
|
return outcome;
|
|
446
456
|
if (outcome.value.id !== raw.id) {
|
|
@@ -333,7 +333,11 @@ async function refuseOutOfScopeUsesFolds(usesCompile, scopePipelines, catalogRoo
|
|
|
333
333
|
catch {
|
|
334
334
|
outsideId = undefined;
|
|
335
335
|
}
|
|
336
|
-
const outcome = await loadPipelineOutcome(pipelinePath, {
|
|
336
|
+
const outcome = await loadPipelineOutcome(pipelinePath, {
|
|
337
|
+
cwd,
|
|
338
|
+
projectRoot,
|
|
339
|
+
requireIo: false,
|
|
340
|
+
});
|
|
337
341
|
if (outcome.ok) {
|
|
338
342
|
const other = new Map();
|
|
339
343
|
const otherErrors = [];
|
|
@@ -415,7 +419,11 @@ async function planMigrateYamlInner(targetPath, options) {
|
|
|
415
419
|
skipped.add(relPath(cwd, pipelinePath));
|
|
416
420
|
continue;
|
|
417
421
|
}
|
|
418
|
-
const outcome = await loadPipelineOutcome(pipelinePath, {
|
|
422
|
+
const outcome = await loadPipelineOutcome(pipelinePath, {
|
|
423
|
+
cwd,
|
|
424
|
+
projectRoot,
|
|
425
|
+
requireIo: false,
|
|
426
|
+
});
|
|
419
427
|
if (!outcome.ok) {
|
|
420
428
|
if (outcome.issues.some((issue) => issue.code === "catalog.mixed_yaml_dialect")) {
|
|
421
429
|
skipped.add(relPath(cwd, pipelinePath));
|
|
@@ -518,7 +526,10 @@ async function planMigrateYamlInner(targetPath, options) {
|
|
|
518
526
|
skipped.add(relPath(cwd, stagePath));
|
|
519
527
|
continue;
|
|
520
528
|
}
|
|
521
|
-
const outcome = await loadStageOutcome(stagePath, {
|
|
529
|
+
const outcome = await loadStageOutcome(stagePath, {
|
|
530
|
+
deferSchemaRefs: true,
|
|
531
|
+
requireIo: false,
|
|
532
|
+
});
|
|
522
533
|
if (!outcome.ok) {
|
|
523
534
|
if (outcome.issues.some((issue) => issue.code === "catalog.mixed_yaml_dialect")) {
|
|
524
535
|
skipped.add(relPath(cwd, stagePath));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CompletionContract, RecoveryPolicy } from "../types/completion.js";
|
|
2
|
-
import type { NormalizedPipelineStageEntry,
|
|
2
|
+
import type { CloneMode, NormalizedPipelineStageEntry, PipelineRouteEntry } from "../types/pipeline.js";
|
|
3
3
|
import { type LoadOutcome } from "./loadOutcome.js";
|
|
4
4
|
import type { RawMergedEntry } from "./mergePipelineIncludes.js";
|
|
5
5
|
export declare function inferIdFromUsesPath(usesPath: string): string | null;
|
|
@@ -9,15 +9,11 @@ export declare function normalizePipelineStageEntries(rawEntries: RawMergedEntry
|
|
|
9
9
|
}): LoadOutcome<NormalizedPipelineStageEntry[]>;
|
|
10
10
|
export declare function toWiringRefs(entries: NormalizedPipelineStageEntry[]): Array<{
|
|
11
11
|
id: string;
|
|
12
|
-
needs?: PipelineNeeds;
|
|
13
|
-
fork?: {
|
|
14
|
-
select: "one" | "subset";
|
|
15
|
-
allow_none?: boolean;
|
|
16
|
-
};
|
|
17
|
-
clonable?: boolean;
|
|
18
|
-
clone_cap?: number;
|
|
19
12
|
completion?: CompletionContract;
|
|
20
13
|
recovery?: RecoveryPolicy;
|
|
21
|
-
feedback_loop?: NormalizedPipelineStageEntry["feedback_loop"];
|
|
22
14
|
replay_safe?: boolean;
|
|
15
|
+
route?: PipelineRouteEntry[];
|
|
16
|
+
entry?: boolean;
|
|
17
|
+
clone_cap?: number;
|
|
18
|
+
clone_mode?: CloneMode;
|
|
23
19
|
}>;
|