@interf/compiler 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -188
- package/builtin-workflows/interf/README.md +22 -10
- package/builtin-workflows/interf/compile/stages/shape/SKILL.md +6 -3
- package/builtin-workflows/interf/compile/stages/structure/SKILL.md +3 -0
- package/builtin-workflows/interf/compile/stages/summarize/SKILL.md +18 -2
- package/builtin-workflows/interf/improve/SKILL.md +2 -2
- package/builtin-workflows/interf/workflow.json +18 -4
- package/builtin-workflows/interf/{compiled.schema.json → workflow.schema.json} +9 -2
- package/dist/commands/check-draft.js +3 -3
- package/dist/commands/compile-controller.js +9 -16
- package/dist/commands/compile.d.ts +19 -1
- package/dist/commands/compile.js +98 -28
- package/dist/commands/create-workflow-wizard.d.ts +20 -2
- package/dist/commands/create-workflow-wizard.js +163 -27
- package/dist/commands/create.d.ts +1 -1
- package/dist/commands/create.js +67 -60
- package/dist/commands/dataset-selection.d.ts +6 -0
- package/dist/commands/dataset-selection.js +11 -0
- package/dist/commands/default.js +3 -3
- package/dist/commands/doctor.js +8 -8
- package/dist/commands/executor-flow.d.ts +1 -1
- package/dist/commands/executor-flow.js +5 -2
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +56 -48
- package/dist/commands/list.js +6 -3
- package/dist/commands/reset.js +1 -1
- package/dist/commands/source-config-wizard.d.ts +2 -2
- package/dist/commands/source-config-wizard.js +50 -17
- package/dist/commands/test-flow.js +5 -16
- package/dist/commands/test.d.ts +0 -6
- package/dist/commands/test.js +9 -17
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/agent-args.d.ts +1 -0
- package/dist/lib/agent-args.js +10 -0
- package/dist/lib/agent-execution.js +2 -1
- package/dist/lib/agent-preflight.js +2 -1
- package/dist/lib/agent-shells.d.ts +26 -1
- package/dist/lib/agent-shells.js +214 -40
- package/dist/lib/agents.d.ts +1 -1
- package/dist/lib/agents.js +1 -1
- package/dist/lib/builtin-compiled-workflow.d.ts +38 -0
- package/dist/lib/builtin-compiled-workflow.js +94 -0
- package/dist/lib/compiled-compile.d.ts +0 -4
- package/dist/lib/compiled-compile.js +11 -30
- package/dist/lib/compiled-paths.d.ts +1 -2
- package/dist/lib/compiled-paths.js +8 -13
- package/dist/lib/compiled-raw.d.ts +2 -2
- package/dist/lib/compiled-reset.d.ts +1 -0
- package/dist/lib/compiled-reset.js +42 -14
- package/dist/lib/compiled-schema.d.ts +11 -7
- package/dist/lib/compiled-schema.js +47 -16
- package/dist/lib/discovery.d.ts +1 -1
- package/dist/lib/discovery.js +2 -2
- package/dist/lib/executors.d.ts +1 -1
- package/dist/lib/executors.js +2 -2
- package/dist/lib/interf-detect.d.ts +0 -1
- package/dist/lib/interf-detect.js +7 -18
- package/dist/lib/interf-scaffold.js +4 -11
- package/dist/lib/interf-workflow-package.d.ts +8 -3
- package/dist/lib/interf-workflow-package.js +128 -62
- package/dist/lib/interf.d.ts +1 -1
- package/dist/lib/interf.js +1 -1
- package/dist/lib/local-workflows.d.ts +4 -3
- package/dist/lib/local-workflows.js +127 -104
- package/dist/lib/project-paths.d.ts +2 -4
- package/dist/lib/project-paths.js +13 -10
- package/dist/lib/runtime-acceptance.js +15 -3
- package/dist/lib/runtime-contracts.js +3 -2
- package/dist/lib/runtime-paths.d.ts +1 -0
- package/dist/lib/runtime-paths.js +4 -1
- package/dist/lib/runtime-prompt.js +4 -4
- package/dist/lib/runtime-reconcile.js +90 -64
- package/dist/lib/runtime-runs.js +29 -102
- package/dist/lib/runtime.d.ts +1 -1
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/schema.d.ts +104 -54
- package/dist/lib/schema.js +32 -116
- package/dist/lib/source-config.js +21 -22
- package/dist/lib/state-health.js +4 -2
- package/dist/lib/state-io.js +2 -110
- package/dist/lib/state-view.js +8 -8
- package/dist/lib/state.d.ts +1 -0
- package/dist/lib/state.js +7 -0
- package/dist/lib/test-execution.js +2 -2
- package/dist/lib/test-paths.js +12 -3
- package/dist/lib/test-sandbox.js +4 -17
- package/dist/lib/test-specs.js +1 -1
- package/dist/lib/validate-compiled.js +13 -8
- package/dist/lib/validate.d.ts +5 -1
- package/dist/lib/validate.js +30 -22
- package/dist/lib/workflow-authoring.d.ts +26 -0
- package/dist/lib/workflow-authoring.js +119 -0
- package/dist/lib/workflow-definitions.d.ts +14 -3
- package/dist/lib/workflow-definitions.js +21 -17
- package/dist/lib/workflow-edit-session.d.ts +16 -0
- package/dist/lib/workflow-edit-session.js +57 -0
- package/dist/lib/workflow-edit-utils.d.ts +10 -0
- package/dist/lib/workflow-edit-utils.js +39 -0
- package/dist/lib/workflow-improvement.js +30 -217
- package/dist/lib/workflow-primitives.d.ts +2 -0
- package/dist/lib/workflow-primitives.js +5 -0
- package/dist/lib/workflow-stage-policy.d.ts +5 -0
- package/dist/lib/workflow-stage-policy.js +31 -0
- package/package.json +7 -8
- package/dist/lib/compiled-layout.d.ts +0 -2
- package/dist/lib/compiled-layout.js +0 -60
- package/dist/lib/obsidian.d.ts +0 -1
- package/dist/lib/obsidian.js +0 -15
- package/dist/lib/summarize-plan.d.ts +0 -17
- package/dist/lib/summarize-plan.js +0 -124
- package/dist/lib/workflow-abi.d.ts +0 -129
- package/dist/lib/workflow-abi.js +0 -156
|
@@ -6,9 +6,10 @@ import { isMarkdownFile } from "./util.js";
|
|
|
6
6
|
import { listFilesRecursive } from "./filesystem.js";
|
|
7
7
|
import { warnInterf } from "./logger.js";
|
|
8
8
|
import { readJsonFileUnchecked, readJsonFileWithSchema } from "./parse.js";
|
|
9
|
-
import { listBuiltinCompiledZoneSpecs, } from "./workflow
|
|
10
|
-
import { WorkflowCompilerApiSchema, RuntimeContractTypeSchema, RuntimeStageAcceptanceSchema,
|
|
11
|
-
import {
|
|
9
|
+
import { listBuiltinCompiledZoneSpecs, } from "./builtin-compiled-workflow.js";
|
|
10
|
+
import { WorkflowCompilerApiSchema, RuntimeContractTypeSchema, RuntimeStageAcceptanceSchema, WorkflowPurposeSchema, WorkflowStageZoneAccessSchema, WorkflowIdPattern, } from "./schema.js";
|
|
11
|
+
import { WORKFLOW_SCHEMA_FILE, workflowSchemaExists, writeWorkflowSchemaDocument, workflowSchemaFilePath, readWorkflowSchemaFile, } from "./compiled-schema.js";
|
|
12
|
+
import { mergeStagePolicyNotesForStages } from "./workflow-stage-policy.js";
|
|
12
13
|
const LocalWorkflowStageDefinitionSchema = z.object({
|
|
13
14
|
id: z.string().regex(WorkflowIdPattern),
|
|
14
15
|
label: z.string().min(1),
|
|
@@ -23,6 +24,7 @@ const LocalWorkflowDefinitionSchema = z.object({
|
|
|
23
24
|
id: z.string().regex(WorkflowIdPattern),
|
|
24
25
|
type: z.literal("compiled"),
|
|
25
26
|
compiler_api: WorkflowCompilerApiSchema.optional(),
|
|
27
|
+
purpose: WorkflowPurposeSchema.optional(),
|
|
26
28
|
label: z.string().min(1),
|
|
27
29
|
hint: z.string().min(1),
|
|
28
30
|
extends: z.string().regex(WorkflowIdPattern).optional(),
|
|
@@ -38,15 +40,49 @@ function builtinWorkflowRootPath(workflowId) {
|
|
|
38
40
|
export function workflowDefinitionPath(sourcePath, id) {
|
|
39
41
|
return join(workflowRootPath(sourcePath), id);
|
|
40
42
|
}
|
|
43
|
+
function isSupportedWorkflowStarterDocPath(relativePath) {
|
|
44
|
+
if (relativePath === "README.md")
|
|
45
|
+
return true;
|
|
46
|
+
if (relativePath.startsWith("improve/"))
|
|
47
|
+
return true;
|
|
48
|
+
if (relativePath.startsWith("use/query/"))
|
|
49
|
+
return true;
|
|
50
|
+
if (relativePath.startsWith("compile/stages/"))
|
|
51
|
+
return true;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
41
54
|
function collectStarterDocs(dirPath) {
|
|
42
55
|
return listFilesRecursive(dirPath, isMarkdownFile)
|
|
43
|
-
.
|
|
44
|
-
.
|
|
45
|
-
relativePath
|
|
46
|
-
|
|
56
|
+
.map((filePath) => relative(dirPath, filePath).replaceAll("\\", "/"))
|
|
57
|
+
.filter((relativePath) => isSupportedWorkflowStarterDocPath(relativePath))
|
|
58
|
+
.map((relativePath) => ({
|
|
59
|
+
relativePath,
|
|
60
|
+
content: readFileSync(join(dirPath, relativePath), "utf8"),
|
|
47
61
|
}))
|
|
48
62
|
.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
49
63
|
}
|
|
64
|
+
function workflowPackageCopyPaths(dirPath) {
|
|
65
|
+
return [
|
|
66
|
+
"workflow.json",
|
|
67
|
+
WORKFLOW_SCHEMA_FILE,
|
|
68
|
+
...collectStarterDocs(dirPath).map((doc) => doc.relativePath),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
function copyRelativeFile(sourceRoot, targetRoot, relativePath) {
|
|
72
|
+
const sourcePath = join(sourceRoot, relativePath);
|
|
73
|
+
if (!existsSync(sourcePath))
|
|
74
|
+
return;
|
|
75
|
+
const targetPath = join(targetRoot, relativePath);
|
|
76
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
77
|
+
cpSync(sourcePath, targetPath, { force: true });
|
|
78
|
+
}
|
|
79
|
+
function copyWorkflowPackageFiles(sourceWorkflowPath, targetWorkflowPath) {
|
|
80
|
+
rmSync(targetWorkflowPath, { recursive: true, force: true });
|
|
81
|
+
mkdirSync(targetWorkflowPath, { recursive: true });
|
|
82
|
+
for (const relativePath of workflowPackageCopyPaths(sourceWorkflowPath)) {
|
|
83
|
+
copyRelativeFile(sourceWorkflowPath, targetWorkflowPath, relativePath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
50
86
|
export function loadWorkflowDefinitionFromDir(dirPath) {
|
|
51
87
|
const workflowPath = existsSync(join(dirPath, "workflow.json"))
|
|
52
88
|
? join(dirPath, "workflow.json")
|
|
@@ -56,9 +92,8 @@ export function loadWorkflowDefinitionFromDir(dirPath) {
|
|
|
56
92
|
const definition = readJsonFileWithSchema(workflowPath, "local workflow definition", LocalWorkflowDefinitionSchema);
|
|
57
93
|
if (!definition)
|
|
58
94
|
return null;
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
if (!compiledSchema)
|
|
95
|
+
const workflowSchema = readWorkflowSchemaFile(dirPath);
|
|
96
|
+
if (!workflowSchema)
|
|
62
97
|
return null;
|
|
63
98
|
if (definition.type !== "compiled") {
|
|
64
99
|
warnInterf(`Warning: local workflow definition at ${workflowPath} has unexpected type "${definition.type}".`);
|
|
@@ -66,11 +101,11 @@ export function loadWorkflowDefinitionFromDir(dirPath) {
|
|
|
66
101
|
}
|
|
67
102
|
return {
|
|
68
103
|
...definition,
|
|
69
|
-
|
|
104
|
+
workflow_schema: workflowSchema,
|
|
70
105
|
starter_docs: collectStarterDocs(dirPath),
|
|
71
106
|
directoryPath: dirPath,
|
|
72
107
|
workflowPath,
|
|
73
|
-
|
|
108
|
+
workflowSchemaPath: workflowSchemaFilePath(dirPath),
|
|
74
109
|
};
|
|
75
110
|
}
|
|
76
111
|
export function listLocalWorkflowDefinitions(sourcePath) {
|
|
@@ -108,36 +143,6 @@ export function resolveWorkflowPackageSourcePath(sourcePath, workflowId) {
|
|
|
108
143
|
export function isWorkflowId(value) {
|
|
109
144
|
return WorkflowIdPattern.test(value);
|
|
110
145
|
}
|
|
111
|
-
function normalizeStagePolicyNotes(value) {
|
|
112
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
113
|
-
return undefined;
|
|
114
|
-
const normalized = {};
|
|
115
|
-
for (const [stageId, notes] of Object.entries(value)) {
|
|
116
|
-
if (!Array.isArray(notes))
|
|
117
|
-
continue;
|
|
118
|
-
const cleaned = Array.from(new Set(notes
|
|
119
|
-
.filter((note) => typeof note === "string")
|
|
120
|
-
.map((note) => note.trim())
|
|
121
|
-
.filter((note) => note.length > 0)));
|
|
122
|
-
if (cleaned.length > 0) {
|
|
123
|
-
normalized[stageId] = cleaned;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
127
|
-
}
|
|
128
|
-
function mergeStagePolicyNotes(currentValue, nextValue) {
|
|
129
|
-
const current = normalizeStagePolicyNotes(currentValue);
|
|
130
|
-
if (!nextValue || Object.keys(nextValue).length === 0)
|
|
131
|
-
return current;
|
|
132
|
-
const merged = { ...(current ?? {}) };
|
|
133
|
-
for (const [stageId, notes] of Object.entries(nextValue)) {
|
|
134
|
-
merged[stageId] = Array.from(new Set([
|
|
135
|
-
...(merged[stageId] ?? []),
|
|
136
|
-
...notes.map((note) => note.trim()).filter((note) => note.length > 0),
|
|
137
|
-
]));
|
|
138
|
-
}
|
|
139
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
140
|
-
}
|
|
141
146
|
function readWorkflowJsonObject(dirPath) {
|
|
142
147
|
const workflowPath = join(dirPath, "workflow.json");
|
|
143
148
|
const raw = readJsonFileUnchecked(workflowPath, "workflow package");
|
|
@@ -148,7 +153,6 @@ function readWorkflowJsonObject(dirPath) {
|
|
|
148
153
|
}
|
|
149
154
|
export function patchWorkflowPackageMetadata(dirPath, options = {}) {
|
|
150
155
|
const workflowPath = join(dirPath, "workflow.json");
|
|
151
|
-
const schemaPath = compiledSchemaFilePath(dirPath);
|
|
152
156
|
const workflowJson = readWorkflowJsonObject(dirPath);
|
|
153
157
|
const normalizedStages = Array.isArray(workflowJson.stages) && workflowJson.stages.length > 0
|
|
154
158
|
? workflowJson.stages
|
|
@@ -169,7 +173,7 @@ export function patchWorkflowPackageMetadata(dirPath, options = {}) {
|
|
|
169
173
|
...(options.hint ? { hint: options.hint } : {}),
|
|
170
174
|
};
|
|
171
175
|
delete nextWorkflowJson.extends;
|
|
172
|
-
const mergedStagePolicyNotes =
|
|
176
|
+
const mergedStagePolicyNotes = mergeStagePolicyNotesForStages(normalizedStages, workflowJson.stage_policy_notes, options.stagePolicyNotes);
|
|
173
177
|
if (mergedStagePolicyNotes) {
|
|
174
178
|
nextWorkflowJson.stage_policy_notes = mergedStagePolicyNotes;
|
|
175
179
|
}
|
|
@@ -177,33 +181,57 @@ export function patchWorkflowPackageMetadata(dirPath, options = {}) {
|
|
|
177
181
|
delete nextWorkflowJson.stage_policy_notes;
|
|
178
182
|
}
|
|
179
183
|
writeFileSync(workflowPath, JSON.stringify(nextWorkflowJson, null, 2) + "\n");
|
|
180
|
-
const schemaLabel = `${String(nextWorkflowJson.label ?? workflowJson.label ?? options.id ?? "Workflow")}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
const schemaLabel = `${String(nextWorkflowJson.label ?? workflowJson.label ?? options.id ?? "Workflow")} workflow schema`;
|
|
185
|
+
const existingSchema = readWorkflowSchemaFile(dirPath);
|
|
186
|
+
if (!existingSchema) {
|
|
187
|
+
throw new Error(`Cannot patch workflow package at ${dirPath}: missing ${WORKFLOW_SCHEMA_FILE}. Restore or reseed the package instead of regenerating a schema from workflow.json.`);
|
|
184
188
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return;
|
|
188
|
-
writeFileSync(schemaPath, JSON.stringify({
|
|
189
|
-
...rawSchema,
|
|
189
|
+
writeWorkflowSchemaDocument(dirPath, {
|
|
190
|
+
...existingSchema,
|
|
190
191
|
label: schemaLabel,
|
|
191
|
-
}
|
|
192
|
+
});
|
|
192
193
|
}
|
|
193
|
-
|
|
194
|
+
function collectWorkflowPackageStructureIssues(dirPath, stages) {
|
|
194
195
|
const issues = [];
|
|
195
196
|
const workflowPath = join(dirPath, "workflow.json");
|
|
196
|
-
const schemaPath = compiledSchemaFilePath(dirPath);
|
|
197
197
|
if (!existsSync(workflowPath))
|
|
198
198
|
issues.push("missing workflow.json");
|
|
199
|
-
if (!
|
|
200
|
-
issues.push("missing
|
|
199
|
+
if (!workflowSchemaExists(dirPath))
|
|
200
|
+
issues.push("missing workflow.schema.json");
|
|
201
201
|
if (!existsSync(join(dirPath, "README.md")))
|
|
202
202
|
issues.push("missing README.md");
|
|
203
203
|
if (!existsSync(join(dirPath, "improve", "SKILL.md")))
|
|
204
204
|
issues.push("missing improve/SKILL.md");
|
|
205
205
|
if (!existsSync(join(dirPath, "use", "query", "SKILL.md")))
|
|
206
206
|
issues.push("missing use/query/SKILL.md");
|
|
207
|
+
if (!stages)
|
|
208
|
+
return issues;
|
|
209
|
+
for (const stage of stages) {
|
|
210
|
+
const skillDir = typeof stage.skill_dir === "string" && stage.skill_dir.trim().length > 0
|
|
211
|
+
? stage.skill_dir
|
|
212
|
+
: stage.id;
|
|
213
|
+
if (!existsSync(join(dirPath, "compile", "stages", skillDir, "SKILL.md"))) {
|
|
214
|
+
issues.push(`missing compile/stages/${skillDir}/SKILL.md`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return issues;
|
|
218
|
+
}
|
|
219
|
+
function collectWorkflowStageSkillIssues(dirPath, stages) {
|
|
220
|
+
if (!stages)
|
|
221
|
+
return [];
|
|
222
|
+
const issues = [];
|
|
223
|
+
for (const stage of stages) {
|
|
224
|
+
const skillDir = typeof stage.skill_dir === "string" && stage.skill_dir.trim().length > 0
|
|
225
|
+
? stage.skill_dir
|
|
226
|
+
: stage.id;
|
|
227
|
+
if (!existsSync(join(dirPath, "compile", "stages", skillDir, "SKILL.md"))) {
|
|
228
|
+
issues.push(`missing compile/stages/${skillDir}/SKILL.md`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return issues;
|
|
232
|
+
}
|
|
233
|
+
export function describeWorkflowPackagePortability(dirPath) {
|
|
234
|
+
const issues = collectWorkflowPackageStructureIssues(dirPath, null);
|
|
207
235
|
if (issues.length > 0)
|
|
208
236
|
return issues;
|
|
209
237
|
let workflowJson;
|
|
@@ -228,15 +256,10 @@ export function describeWorkflowPackagePortability(dirPath) {
|
|
|
228
256
|
issues.push("workflow.json must declare explicit stages for a portable workflow package");
|
|
229
257
|
return issues;
|
|
230
258
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (!existsSync(join(dirPath, "compile", "stages", skillDir, "SKILL.md"))) {
|
|
236
|
-
issues.push(`missing compile/stages/${skillDir}/SKILL.md`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return issues;
|
|
259
|
+
return [
|
|
260
|
+
...issues,
|
|
261
|
+
...collectWorkflowStageSkillIssues(dirPath, parsed.data.stages ?? null),
|
|
262
|
+
];
|
|
240
263
|
}
|
|
241
264
|
export function isPortableWorkflowPackage(dirPath) {
|
|
242
265
|
return describeWorkflowPackagePortability(dirPath).length === 0;
|
|
@@ -246,13 +269,11 @@ export function copyWorkflowPackageDirectory(sourceWorkflowPath, targetWorkflowP
|
|
|
246
269
|
if (portabilityIssues.length > 0) {
|
|
247
270
|
throw new Error(`Workflow package at ${sourceWorkflowPath} is not directly copyable: ${portabilityIssues.join("; ")}.`);
|
|
248
271
|
}
|
|
249
|
-
rmSync(targetWorkflowPath, { recursive: true, force: true });
|
|
250
272
|
mkdirSync(dirname(targetWorkflowPath), { recursive: true });
|
|
251
|
-
|
|
273
|
+
copyWorkflowPackageFiles(sourceWorkflowPath, targetWorkflowPath);
|
|
252
274
|
}
|
|
253
275
|
export function validateWorkflowPackage(dirPath) {
|
|
254
276
|
const workflowPath = join(dirPath, "workflow.json");
|
|
255
|
-
const schemaPath = compiledSchemaFilePath(dirPath);
|
|
256
277
|
if (!existsSync(workflowPath)) {
|
|
257
278
|
return {
|
|
258
279
|
ok: false,
|
|
@@ -288,38 +309,30 @@ export function validateWorkflowPackage(dirPath) {
|
|
|
288
309
|
if (!def.stages || def.stages.length === 0) {
|
|
289
310
|
errors.push("workflow.json must declare explicit stages. Legacy inherited packages are not portable.");
|
|
290
311
|
}
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
if (!existsSync(join(dirPath, "improve", "SKILL.md"))) {
|
|
298
|
-
errors.push("Missing improve/SKILL.md.");
|
|
299
|
-
}
|
|
300
|
-
if (!existsSync(join(dirPath, "use", "query", "SKILL.md"))) {
|
|
301
|
-
errors.push("Missing use/query/SKILL.md.");
|
|
302
|
-
}
|
|
303
|
-
const compiledSchema = readJsonFileWithSchema(schemaPath, "compiled schema", WorkflowCompiledSchemaSchema);
|
|
304
|
-
if (!compiledSchema) {
|
|
305
|
-
errors.push("compiled.schema.json is missing or invalid.");
|
|
306
|
-
}
|
|
307
|
-
if (def.stages && def.stages.length > 0) {
|
|
308
|
-
for (const stage of def.stages) {
|
|
309
|
-
const skillDir = typeof stage.skill_dir === "string" && stage.skill_dir.trim().length > 0
|
|
310
|
-
? stage.skill_dir
|
|
311
|
-
: stage.id;
|
|
312
|
-
if (!existsSync(join(dirPath, "compile", "stages", skillDir, "SKILL.md"))) {
|
|
313
|
-
errors.push(`Missing compile/stages/${skillDir}/SKILL.md.`);
|
|
312
|
+
if (def.stage_policy_notes && def.stages) {
|
|
313
|
+
const stageIds = new Set(def.stages.map((stage) => stage.id));
|
|
314
|
+
for (const stageId of Object.keys(def.stage_policy_notes)) {
|
|
315
|
+
if (!stageIds.has(stageId)) {
|
|
316
|
+
errors.push(`workflow.json stage_policy_notes references unknown stage "${stageId}".`);
|
|
314
317
|
}
|
|
315
318
|
}
|
|
316
319
|
}
|
|
317
|
-
|
|
320
|
+
for (const issue of collectWorkflowPackageStructureIssues(dirPath, def.stages ?? null)) {
|
|
321
|
+
const formatted = issue.startsWith("missing ")
|
|
322
|
+
? `Missing ${issue.slice("missing ".length)}.`
|
|
323
|
+
: issue;
|
|
324
|
+
errors.push(formatted);
|
|
325
|
+
}
|
|
326
|
+
const workflowSchema = readWorkflowSchemaFile(dirPath);
|
|
327
|
+
if (!workflowSchema) {
|
|
328
|
+
errors.push("workflow.schema.json is missing or invalid.");
|
|
329
|
+
}
|
|
330
|
+
if (workflowSchema) {
|
|
318
331
|
const stages = def.stages ?? [];
|
|
319
332
|
const stageIds = new Set(stages.map((stage) => stage.id));
|
|
320
333
|
const seenPaths = new Map();
|
|
321
334
|
const seenZoneIds = new Set();
|
|
322
|
-
const zoneById = new Map(
|
|
335
|
+
const zoneById = new Map(workflowSchema.zones.map((zone) => [zone.id, zone]));
|
|
323
336
|
const normalizedParts = (value) => value.replaceAll("\\", "/").split("/").filter(Boolean);
|
|
324
337
|
const hasOverlappingPath = (value, other) => {
|
|
325
338
|
const a = normalizedParts(value);
|
|
@@ -328,26 +341,29 @@ export function validateWorkflowPackage(dirPath) {
|
|
|
328
341
|
const longer = a.length <= b.length ? b : a;
|
|
329
342
|
return shorter.every((part, index) => longer[index] === part);
|
|
330
343
|
};
|
|
331
|
-
for (const zone of
|
|
344
|
+
for (const zone of workflowSchema.zones) {
|
|
332
345
|
if (seenZoneIds.has(zone.id)) {
|
|
333
|
-
errors.push(`
|
|
346
|
+
errors.push(`workflow.schema.json repeats zone id "${zone.id}".`);
|
|
334
347
|
}
|
|
335
348
|
seenZoneIds.add(zone.id);
|
|
336
349
|
const existingPathOwner = seenPaths.get(zone.path);
|
|
337
350
|
if (existingPathOwner) {
|
|
338
|
-
errors.push(`
|
|
351
|
+
errors.push(`workflow.schema.json repeats zone path "${zone.path}".`);
|
|
339
352
|
}
|
|
340
353
|
for (const [existingPath, existingZoneId] of seenPaths.entries()) {
|
|
341
354
|
if (existingPath === zone.path)
|
|
342
355
|
continue;
|
|
343
356
|
if (hasOverlappingPath(zone.path, existingPath)) {
|
|
344
|
-
errors.push(`
|
|
357
|
+
errors.push(`workflow.schema.json zones "${zone.id}" and "${existingZoneId}" overlap on path "${zone.path}" / "${existingPath}".`);
|
|
345
358
|
}
|
|
346
359
|
}
|
|
347
360
|
seenPaths.set(zone.path, zone.id);
|
|
361
|
+
if ((zone.role === "input" || zone.role === "runtime") && zone.owned_by.length > 0) {
|
|
362
|
+
errors.push(`workflow.schema.json zone "${zone.id}" cannot declare owners because its role is "${zone.role}".`);
|
|
363
|
+
}
|
|
348
364
|
for (const owner of zone.owned_by) {
|
|
349
365
|
if (!stageIds.has(owner)) {
|
|
350
|
-
errors.push(`
|
|
366
|
+
errors.push(`workflow.schema.json references unknown stage "${owner}" for zone "${zone.path}".`);
|
|
351
367
|
}
|
|
352
368
|
}
|
|
353
369
|
}
|
|
@@ -363,19 +379,25 @@ export function validateWorkflowPackage(dirPath) {
|
|
|
363
379
|
errors.push(`Stage "${stage.id}" writes unknown zone "${zoneId}".`);
|
|
364
380
|
continue;
|
|
365
381
|
}
|
|
382
|
+
if (zone.role === "input" || zone.role === "runtime") {
|
|
383
|
+
errors.push(`Stage "${stage.id}" writes zone "${zoneId}" but zones with role "${zone.role}" are engine-owned inputs, not writable stage outputs.`);
|
|
384
|
+
}
|
|
366
385
|
if (!zone.owned_by.includes(stage.id)) {
|
|
367
|
-
errors.push(`Stage "${stage.id}" writes zone "${zoneId}" but that zone is not owned by the stage in
|
|
386
|
+
errors.push(`Stage "${stage.id}" writes zone "${zoneId}" but that zone is not owned by the stage in workflow.schema.json.`);
|
|
368
387
|
}
|
|
369
388
|
}
|
|
370
389
|
}
|
|
371
390
|
for (const requiredZone of listBuiltinCompiledZoneSpecs().filter((zone) => zone.id === "raw" || zone.id === "runtime")) {
|
|
372
391
|
const match = zoneById.get(requiredZone.id);
|
|
373
392
|
if (!match) {
|
|
374
|
-
errors.push(`
|
|
393
|
+
errors.push(`workflow.schema.json is missing required zone "${requiredZone.id}".`);
|
|
375
394
|
continue;
|
|
376
395
|
}
|
|
377
396
|
if (match.kind !== requiredZone.kind) {
|
|
378
|
-
errors.push(`
|
|
397
|
+
errors.push(`workflow.schema.json zone "${requiredZone.id}" should be kind "${requiredZone.kind}".`);
|
|
398
|
+
}
|
|
399
|
+
if (match.role !== requiredZone.role) {
|
|
400
|
+
errors.push(`workflow.schema.json zone "${requiredZone.id}" should have role "${requiredZone.role}".`);
|
|
379
401
|
}
|
|
380
402
|
}
|
|
381
403
|
}
|
|
@@ -385,7 +407,8 @@ export function validateWorkflowPackage(dirPath) {
|
|
|
385
407
|
const counts = {
|
|
386
408
|
starter_docs: collectStarterDocs(dirPath).length,
|
|
387
409
|
compile_stage_docs: stageDirs.length,
|
|
388
|
-
compiled_zones:
|
|
410
|
+
compiled_zones: workflowSchema?.zones.length ?? 0,
|
|
411
|
+
workflow_zones: workflowSchema?.zones.length ?? 0,
|
|
389
412
|
};
|
|
390
413
|
return {
|
|
391
414
|
ok: errors.length === 0,
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
export declare const PROJECT_INTERF_DIR = "interf";
|
|
2
|
-
export declare const PROJECT_WORKFLOW_DIR = "workflows";
|
|
3
|
-
export declare const PROJECT_COMPILED_DIR = "compiled";
|
|
4
2
|
export declare const PROJECT_TESTS_DIR = "tests";
|
|
5
3
|
export declare function projectInterfRoot(projectPath: string): string;
|
|
6
|
-
export declare function projectWorkflowRoot(projectPath: string): string;
|
|
7
|
-
export declare function datasetArtifactRoot(projectPath: string, datasetName: string): string;
|
|
8
4
|
export declare function compiledCompiledPathForDataset(projectPath: string, datasetName: string): string;
|
|
9
5
|
export declare function datasetTestsRoot(projectPath: string, datasetName: string): string;
|
|
10
6
|
export type DatasetTestTargetLabel = "file-as-is" | "compiled";
|
|
11
7
|
export declare function datasetTestRunsRoot(projectPath: string, datasetName: string, target: DatasetTestTargetLabel): string;
|
|
12
8
|
export declare function datasetLatestTestStatePath(projectPath: string, datasetName: string): string;
|
|
13
9
|
export declare function datasetLatestTestSummaryPath(projectPath: string, datasetName: string): string;
|
|
10
|
+
export declare function normalizeDatasetTestRunId(input: string): string;
|
|
11
|
+
export declare function datasetTestRunPath(projectPath: string, datasetName: string, target: DatasetTestTargetLabel, generatedAt: string, runId: string, runSuffix?: string | null): string;
|
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
export const PROJECT_INTERF_DIR = "interf";
|
|
3
|
-
export const PROJECT_WORKFLOW_DIR = "workflows";
|
|
4
|
-
export const PROJECT_COMPILED_DIR = "compiled";
|
|
5
3
|
export const PROJECT_TESTS_DIR = "tests";
|
|
6
4
|
export function projectInterfRoot(projectPath) {
|
|
7
5
|
return join(projectPath, PROJECT_INTERF_DIR);
|
|
8
6
|
}
|
|
9
|
-
export function projectWorkflowRoot(projectPath) {
|
|
10
|
-
return join(projectInterfRoot(projectPath), PROJECT_WORKFLOW_DIR);
|
|
11
|
-
}
|
|
12
|
-
export function datasetArtifactRoot(projectPath, datasetName) {
|
|
13
|
-
return join(projectInterfRoot(projectPath), datasetName);
|
|
14
|
-
}
|
|
15
7
|
export function compiledCompiledPathForDataset(projectPath, datasetName) {
|
|
16
|
-
return join(
|
|
8
|
+
return join(projectInterfRoot(projectPath), datasetName);
|
|
17
9
|
}
|
|
18
10
|
export function datasetTestsRoot(projectPath, datasetName) {
|
|
19
|
-
return join(
|
|
11
|
+
return join(projectInterfRoot(projectPath), PROJECT_TESTS_DIR, datasetName);
|
|
20
12
|
}
|
|
21
13
|
export function datasetTestRunsRoot(projectPath, datasetName, target) {
|
|
22
14
|
return join(datasetTestsRoot(projectPath, datasetName), target, "runs");
|
|
@@ -27,3 +19,14 @@ export function datasetLatestTestStatePath(projectPath, datasetName) {
|
|
|
27
19
|
export function datasetLatestTestSummaryPath(projectPath, datasetName) {
|
|
28
20
|
return join(datasetTestsRoot(projectPath, datasetName), "latest.md");
|
|
29
21
|
}
|
|
22
|
+
export function normalizeDatasetTestRunId(input) {
|
|
23
|
+
return input
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.trim()
|
|
26
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
27
|
+
.replace(/^-+|-+$/g, "")
|
|
28
|
+
.slice(0, 80);
|
|
29
|
+
}
|
|
30
|
+
export function datasetTestRunPath(projectPath, datasetName, target, generatedAt, runId, runSuffix) {
|
|
31
|
+
return join(datasetTestRunsRoot(projectPath, datasetName, target), `${generatedAt.replace(/[:.]/g, "-")}-${runId}${runSuffix ? `-${normalizeDatasetTestRunId(runSuffix)}` : ""}.json`);
|
|
32
|
+
}
|
|
@@ -2,7 +2,7 @@ import { existsSync, } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { listFilesRecursive } from "./filesystem.js";
|
|
4
4
|
import { readJsonFileWithSchema } from "./parse.js";
|
|
5
|
-
import { compiledZoneAbsolutePath, findCompiledSchemaZone, readCompiledSchemaFile, } from "./compiled-schema.js";
|
|
5
|
+
import { WORKFLOW_SCHEMA_FILE, compiledZoneAbsolutePath, findCompiledSchemaZone, readCompiledSchemaFile, } from "./compiled-schema.js";
|
|
6
6
|
import { RuntimeStageContractSchema, } from "./schema.js";
|
|
7
7
|
import { stageContractPath } from "./runtime-paths.js";
|
|
8
8
|
import { workflowPackagePathForCompiled } from "./compiled-paths.js";
|
|
@@ -24,6 +24,7 @@ function hasAcceptanceRules(acceptance) {
|
|
|
24
24
|
Object.keys(acceptance.zone_counts_at_least ?? {}).length > 0 ||
|
|
25
25
|
Object.keys(acceptance.zone_counts_at_least_counts ?? {}).length > 0 ||
|
|
26
26
|
(acceptance.markdown_frontmatter_valid_zones?.length ?? 0) > 0 ||
|
|
27
|
+
Object.keys(acceptance.frontmatter_required_keys_in_zones ?? {}).length > 0 ||
|
|
27
28
|
(acceptance.markdown_abstract_valid_zones?.length ?? 0) > 0 ||
|
|
28
29
|
(acceptance.wikilinks_valid_in_zones?.length ?? 0) > 0 ||
|
|
29
30
|
Object.keys(acceptance.artifacts_must_not_contain ?? {}).length > 0;
|
|
@@ -106,8 +107,8 @@ export function validateResolvedStageAcceptance(dirPath, options) {
|
|
|
106
107
|
if (!schema) {
|
|
107
108
|
return {
|
|
108
109
|
ok: false,
|
|
109
|
-
summary: `Stage acceptance failed for ${options.stageId}: missing
|
|
110
|
-
failures: [
|
|
110
|
+
summary: `Stage acceptance failed for ${options.stageId}: missing workflow schema.`,
|
|
111
|
+
failures: [`Missing workflow/${WORKFLOW_SCHEMA_FILE}.`],
|
|
111
112
|
};
|
|
112
113
|
}
|
|
113
114
|
const failures = [];
|
|
@@ -190,6 +191,17 @@ export function validateResolvedStageAcceptance(dirPath, options) {
|
|
|
190
191
|
failures.push(`Zone "${zoneId}" has ${validation.invalid_frontmatter} markdown file(s) with invalid or incomplete frontmatter.`);
|
|
191
192
|
}
|
|
192
193
|
}
|
|
194
|
+
for (const [zoneId, requiredKeys] of Object.entries(acceptance?.frontmatter_required_keys_in_zones ?? {})) {
|
|
195
|
+
const zone = findCompiledSchemaZone(schema, zoneId);
|
|
196
|
+
if (!zone) {
|
|
197
|
+
failures.push(`Acceptance references unknown zone "${zoneId}".`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const validation = validateSynthFiles(markdownFilesForZone(dirPath, zone.path, zone.kind), { requiredFrontmatterKeys: requiredKeys });
|
|
201
|
+
if (validation.invalid_frontmatter > 0) {
|
|
202
|
+
failures.push(`Zone "${zoneId}" has ${validation.invalid_frontmatter} markdown file(s) missing required frontmatter keys (${requiredKeys.join(", ")}).`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
193
205
|
for (const zoneId of acceptance?.markdown_abstract_valid_zones ?? []) {
|
|
194
206
|
const zone = findCompiledSchemaZone(schema, zoneId);
|
|
195
207
|
if (!zone) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WORKFLOW_SCHEMA_FILE } from "./compiled-schema.js";
|
|
1
2
|
export function buildRuntimeStageContract(options) {
|
|
2
3
|
const localSkillDocs = options.localSkillDocs ?? [];
|
|
3
4
|
const workflowNotes = options.workflowNotes ?? [];
|
|
@@ -11,7 +12,7 @@ export function buildRuntimeStageContract(options) {
|
|
|
11
12
|
artifacts: {
|
|
12
13
|
reads: [
|
|
13
14
|
"interf.json",
|
|
14
|
-
|
|
15
|
+
`workflow/${WORKFLOW_SCHEMA_FILE}`,
|
|
15
16
|
...(options.extraReadArtifacts ?? []),
|
|
16
17
|
...options.stageReadArtifacts,
|
|
17
18
|
...localSkillDocs,
|
|
@@ -27,7 +28,7 @@ export function buildRuntimeStageContract(options) {
|
|
|
27
28
|
notes: [
|
|
28
29
|
`This is the "${options.stageLabel}" stage for the compiled dataset "${options.compiledName}".`,
|
|
29
30
|
"The workflow package is the authoritative method layer for this run.",
|
|
30
|
-
|
|
31
|
+
`Use \`workflow/${WORKFLOW_SCHEMA_FILE}\` as the deterministic zone-contract reference for this compiled dataset.`,
|
|
31
32
|
"Honor the declared read and write artifacts instead of inventing a parallel workflow.",
|
|
32
33
|
"Only create or update files that fall under the declared write targets for this stage.",
|
|
33
34
|
"The CLI owns `.interf/runtime/` ledgers such as run, state, health, and view-spec files. Read them if needed, but do not create, edit, or replace them in this stage unless the contract explicitly marks a runtime artifact as stage-owned.",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare function runPath(dirPath: string): string;
|
|
2
2
|
export declare function runHistoryPath(dirPath: string): string;
|
|
3
3
|
export declare function stageContractPath(dirPath: string): string;
|
|
4
|
+
export declare function archivedStageContractPath(dirPath: string, runId: string): string;
|
|
4
5
|
export declare function logsDirPath(dirPath: string): string;
|
|
5
6
|
export declare function promptLogPath(dirPath: string, runId: string): string;
|
|
6
7
|
export declare function eventLogPath(dirPath: string, runId: string): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { compiledRuntimeLogsRoot, compiledRuntimeRunHistoryPath, compiledRuntimeRunPath, compiledRuntimeStageContractPath, } from "./compiled-paths.js";
|
|
2
|
+
import { compiledRuntimeArchivedStageContractPath, compiledRuntimeLogsRoot, compiledRuntimeRunHistoryPath, compiledRuntimeRunPath, compiledRuntimeStageContractPath, } from "./compiled-paths.js";
|
|
3
3
|
export function runPath(dirPath) {
|
|
4
4
|
return compiledRuntimeRunPath(dirPath);
|
|
5
5
|
}
|
|
@@ -9,6 +9,9 @@ export function runHistoryPath(dirPath) {
|
|
|
9
9
|
export function stageContractPath(dirPath) {
|
|
10
10
|
return compiledRuntimeStageContractPath(dirPath);
|
|
11
11
|
}
|
|
12
|
+
export function archivedStageContractPath(dirPath, runId) {
|
|
13
|
+
return compiledRuntimeArchivedStageContractPath(dirPath, runId);
|
|
14
|
+
}
|
|
12
15
|
export function logsDirPath(dirPath) {
|
|
13
16
|
return compiledRuntimeLogsRoot(dirPath);
|
|
14
17
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export function buildStagePrompt(instructions, contractPath, statusLines) {
|
|
2
|
-
const stageSkillDir = instructions.stage_skill_dir ||
|
|
3
|
-
(instructions.bundled_skill?.split(/[\\/]/).filter(Boolean).pop()) ||
|
|
4
|
-
"stage";
|
|
2
|
+
const stageSkillDir = instructions.stage_skill_dir || "stage";
|
|
5
3
|
const modeNotes = instructions.effective_mode === "override"
|
|
6
4
|
? [
|
|
7
5
|
"Local stage instruction docs are authoritative for this run.",
|
|
@@ -27,11 +25,13 @@ export function buildStagePrompt(instructions, contractPath, statusLines) {
|
|
|
27
25
|
"For file-zone mounts, `runtime/paths.json` also gives the exact file path inside the `inputs/` or `outputs/` mount root.",
|
|
28
26
|
"Honor the contract's counts, artifact paths, and policies instead of inventing another workflow.",
|
|
29
27
|
"Read only the files named by the contract's `artifacts.reads` list plus any paths they explicitly direct you to open.",
|
|
28
|
+
"Prefer direct file-reading and search tools over shell commands when you inspect inputs, docs, or generated outputs.",
|
|
29
|
+
"Do not use shell helpers like `cat`, `sed`, `ls`, or `find` for routine file inspection if a native read/search tool can do the same job.",
|
|
30
30
|
"If the contract lists `instructions.local_docs`, open every one of those files before you write artifacts or declare the stage complete.",
|
|
31
31
|
"If `AGENTS.md` is listed in the contract, use it only for compiled routing that is directly relevant to this stage.",
|
|
32
32
|
"`.interf/runtime/run.json`, `.interf/runtime/state.json`, `.interf/runtime/health.json`, and `.interf/runtime/view-spec.json` are CLI-owned runtime artifacts. You may read them, but do not create, edit, or replace them unless the contract explicitly marks a different proof artifact as stage-owned.",
|
|
33
33
|
"If a contract-listed output file does not exist yet, create it in one whole-file write. Do not attempt patch-style edits against a missing runtime path.",
|
|
34
|
-
"Stay inside the current compiled dataset. Do not try to open
|
|
34
|
+
"Stay inside the current compiled dataset. Do not try to open repo docs, repo source files, or other paths outside the compiled dataset unless the stage contract explicitly requires them.",
|
|
35
35
|
instructions.effective_mode === "override"
|
|
36
36
|
? "Do not depend on any repo-root or globally installed skill cache for this run. Local stage instruction docs are the primary workflow layer for this run."
|
|
37
37
|
: "Do not depend on any repo-root or globally installed skill cache for this run. Use the current compiled workflow docs and the contract as the default behavior.",
|