@exaudeus/workrail 3.0.0 → 3.1.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/dist/application/services/compiler/resolve-templates.d.ts +5 -0
- package/dist/application/services/compiler/resolve-templates.js +35 -0
- package/dist/application/services/compiler/routine-loader.d.ts +11 -0
- package/dist/application/services/compiler/routine-loader.js +45 -0
- package/dist/application/services/compiler/template-registry.d.ts +2 -2
- package/dist/application/services/compiler/template-registry.js +71 -62
- package/dist/application/services/workflow-compiler.d.ts +1 -5
- package/dist/application/services/workflow-compiler.js +39 -21
- package/dist/infrastructure/storage/file-workflow-storage.d.ts +0 -1
- package/dist/infrastructure/storage/file-workflow-storage.js +0 -10
- package/dist/manifest.json +24 -16
- package/package.json +1 -1
- package/workflows/coding-task-workflow-agentic.lean.v2.json +41 -3
- package/workflows/examples/routine-injection-example.json +28 -0
|
@@ -9,5 +9,10 @@ export type ResolveTemplatesPassError = {
|
|
|
9
9
|
readonly code: 'TEMPLATE_EXPAND_ERROR';
|
|
10
10
|
readonly stepId: string;
|
|
11
11
|
readonly cause: TemplateExpandError;
|
|
12
|
+
} | {
|
|
13
|
+
readonly code: 'DUPLICATE_STEP_ID';
|
|
14
|
+
readonly stepId: string;
|
|
15
|
+
readonly templateId: string;
|
|
16
|
+
readonly message: string;
|
|
12
17
|
};
|
|
13
18
|
export declare function resolveTemplatesPass(steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[], registry: TemplateRegistry): Result<readonly (WorkflowStepDefinition | LoopStepDefinition)[], ResolveTemplatesPassError>;
|
|
@@ -23,10 +23,17 @@ function resolveStepTemplate(step, registry) {
|
|
|
23
23
|
cause: expandResult.error,
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
+
if (step.runCondition) {
|
|
27
|
+
return (0, neverthrow_1.ok)(expandResult.value.map(expanded => ({
|
|
28
|
+
...expanded,
|
|
29
|
+
runCondition: expanded.runCondition ?? step.runCondition,
|
|
30
|
+
})));
|
|
31
|
+
}
|
|
26
32
|
return (0, neverthrow_1.ok)(expandResult.value);
|
|
27
33
|
}
|
|
28
34
|
function resolveTemplatesPass(steps, registry) {
|
|
29
35
|
const resolved = [];
|
|
36
|
+
const seenIds = new Set();
|
|
30
37
|
for (const step of steps) {
|
|
31
38
|
if ((0, workflow_definition_js_1.isLoopStepDefinition)(step)) {
|
|
32
39
|
if (Array.isArray(step.body)) {
|
|
@@ -35,11 +42,22 @@ function resolveTemplatesPass(steps, registry) {
|
|
|
35
42
|
const res = resolveStepTemplate(bodyStep, registry);
|
|
36
43
|
if (res.isErr())
|
|
37
44
|
return (0, neverthrow_1.err)(res.error);
|
|
45
|
+
for (const expanded of res.value) {
|
|
46
|
+
const collision = checkIdCollision(expanded.id, bodyStep.templateCall?.templateId, seenIds);
|
|
47
|
+
if (collision)
|
|
48
|
+
return (0, neverthrow_1.err)(collision);
|
|
49
|
+
}
|
|
38
50
|
bodyResolved.push(...res.value);
|
|
39
51
|
}
|
|
52
|
+
const loopCollision = checkIdCollision(step.id, undefined, seenIds);
|
|
53
|
+
if (loopCollision)
|
|
54
|
+
return (0, neverthrow_1.err)(loopCollision);
|
|
40
55
|
resolved.push({ ...step, body: bodyResolved });
|
|
41
56
|
}
|
|
42
57
|
else {
|
|
58
|
+
const loopCollision = checkIdCollision(step.id, undefined, seenIds);
|
|
59
|
+
if (loopCollision)
|
|
60
|
+
return (0, neverthrow_1.err)(loopCollision);
|
|
43
61
|
resolved.push(step);
|
|
44
62
|
}
|
|
45
63
|
}
|
|
@@ -47,8 +65,25 @@ function resolveTemplatesPass(steps, registry) {
|
|
|
47
65
|
const res = resolveStepTemplate(step, registry);
|
|
48
66
|
if (res.isErr())
|
|
49
67
|
return (0, neverthrow_1.err)(res.error);
|
|
68
|
+
for (const expanded of res.value) {
|
|
69
|
+
const collision = checkIdCollision(expanded.id, step.templateCall?.templateId, seenIds);
|
|
70
|
+
if (collision)
|
|
71
|
+
return (0, neverthrow_1.err)(collision);
|
|
72
|
+
}
|
|
50
73
|
resolved.push(...res.value);
|
|
51
74
|
}
|
|
52
75
|
}
|
|
53
76
|
return (0, neverthrow_1.ok)(resolved);
|
|
54
77
|
}
|
|
78
|
+
function checkIdCollision(stepId, templateId, seenIds) {
|
|
79
|
+
if (seenIds.has(stepId)) {
|
|
80
|
+
return {
|
|
81
|
+
code: 'DUPLICATE_STEP_ID',
|
|
82
|
+
stepId,
|
|
83
|
+
templateId: templateId ?? '(none)',
|
|
84
|
+
message: `Duplicate step id '${stepId}' after template expansion${templateId ? ` (from template '${templateId}')` : ''}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
seenIds.add(stepId);
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Result } from 'neverthrow';
|
|
2
|
+
import type { WorkflowDefinition } from '../../../types/workflow-definition.js';
|
|
3
|
+
export interface RoutineLoadWarning {
|
|
4
|
+
readonly file: string;
|
|
5
|
+
readonly reason: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RoutineLoadResult {
|
|
8
|
+
readonly routines: ReadonlyMap<string, WorkflowDefinition>;
|
|
9
|
+
readonly warnings: readonly RoutineLoadWarning[];
|
|
10
|
+
}
|
|
11
|
+
export declare function loadRoutineDefinitions(routinesDir?: string): Result<RoutineLoadResult, string>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadRoutineDefinitions = loadRoutineDefinitions;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const neverthrow_1 = require("neverthrow");
|
|
10
|
+
const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
|
|
11
|
+
function resolveRoutinesDir() {
|
|
12
|
+
const projectRoot = path_1.default.resolve(__dirname, '..', '..', '..', '..');
|
|
13
|
+
return path_1.default.join(projectRoot, 'workflows', 'routines');
|
|
14
|
+
}
|
|
15
|
+
function loadRoutineDefinitions(routinesDir) {
|
|
16
|
+
const dir = routinesDir ?? resolveRoutinesDir();
|
|
17
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
18
|
+
return (0, neverthrow_1.ok)({ routines: new Map(), warnings: [] });
|
|
19
|
+
}
|
|
20
|
+
let files;
|
|
21
|
+
try {
|
|
22
|
+
files = (0, fs_1.readdirSync)(dir).filter(f => f.endsWith('.json'));
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
return (0, neverthrow_1.err)(`Failed to read routines directory '${dir}': ${e}`);
|
|
26
|
+
}
|
|
27
|
+
const routines = new Map();
|
|
28
|
+
const warnings = [];
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
const filePath = path_1.default.join(dir, file);
|
|
31
|
+
try {
|
|
32
|
+
const raw = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
if (!(0, workflow_definition_js_1.hasWorkflowDefinitionShape)(parsed)) {
|
|
35
|
+
warnings.push({ file, reason: 'does not match WorkflowDefinition shape (missing id, name, description, version, or steps)' });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
routines.set(parsed.id, parsed);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
warnings.push({ file, reason: String(e) });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return (0, neverthrow_1.ok)({ routines, warnings });
|
|
45
|
+
}
|
|
@@ -16,6 +16,6 @@ export interface TemplateRegistry {
|
|
|
16
16
|
readonly has: (templateId: string) => boolean;
|
|
17
17
|
readonly knownIds: () => readonly string[];
|
|
18
18
|
}
|
|
19
|
-
export declare function createRoutineExpander(routineId: string, definition: WorkflowDefinition): Result<TemplateExpander, TemplateExpandError>;
|
|
20
19
|
export declare function routineIdToTemplateId(routineId: string): string;
|
|
21
|
-
export declare function
|
|
20
|
+
export declare function createRoutineExpander(routineId: string, definition: WorkflowDefinition): Result<TemplateExpander, TemplateExpandError>;
|
|
21
|
+
export declare function createTemplateRegistry(routineExpanders?: ReadonlyMap<string, TemplateExpander>): TemplateRegistry;
|
|
@@ -1,78 +1,95 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createRoutineExpander = createRoutineExpander;
|
|
4
3
|
exports.routineIdToTemplateId = routineIdToTemplateId;
|
|
4
|
+
exports.createRoutineExpander = createRoutineExpander;
|
|
5
5
|
exports.createTemplateRegistry = createTemplateRegistry;
|
|
6
6
|
const neverthrow_1 = require("neverthrow");
|
|
7
7
|
const SINGLE_BRACE_ARG = /(?<!\{)\{([^{}]+)\}(?!\})/g;
|
|
8
|
-
function
|
|
8
|
+
function isSubstitutableValue(value) {
|
|
9
|
+
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
10
|
+
}
|
|
11
|
+
function substituteArgs(template, args, templateId, routineId, stepId) {
|
|
9
12
|
const missing = [];
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const badType = [];
|
|
14
|
+
const result = template.replace(SINGLE_BRACE_ARG, (match, argName) => {
|
|
15
|
+
if (!(argName in args)) {
|
|
16
|
+
missing.push(argName);
|
|
17
|
+
return match;
|
|
13
18
|
}
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
const value = args[argName];
|
|
20
|
+
if (!isSubstitutableValue(value)) {
|
|
21
|
+
badType.push(argName);
|
|
22
|
+
return match;
|
|
23
|
+
}
|
|
24
|
+
return String(value);
|
|
16
25
|
});
|
|
17
26
|
if (missing.length > 0) {
|
|
18
27
|
return (0, neverthrow_1.err)({
|
|
19
28
|
code: 'TEMPLATE_EXPAND_FAILED',
|
|
20
|
-
templateId
|
|
29
|
+
templateId,
|
|
21
30
|
message: `MISSING_TEMPLATE_ARG: routine '${routineId}' step '${stepId}' references arg(s) '${missing.join("', '")}' but they were not provided in templateCall.args`,
|
|
22
31
|
});
|
|
23
32
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
code: 'TEMPLATE_EXPAND_FAILED',
|
|
31
|
-
templateId: `wr.templates.routine.${routineId}`,
|
|
32
|
-
message: `Routine '${routineId}' step '${step.id}' contains a templateCall. Recursive routine injection is not supported.`,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
33
|
+
if (badType.length > 0) {
|
|
34
|
+
return (0, neverthrow_1.err)({
|
|
35
|
+
code: 'TEMPLATE_EXPAND_FAILED',
|
|
36
|
+
templateId,
|
|
37
|
+
message: `INVALID_TEMPLATE_ARG_TYPE: routine '${routineId}' step '${stepId}' arg(s) '${badType.join("', '")}' must be string, number, or boolean (got non-primitive)`,
|
|
38
|
+
});
|
|
35
39
|
}
|
|
36
|
-
return (0, neverthrow_1.ok)(
|
|
40
|
+
return (0, neverthrow_1.ok)(result);
|
|
41
|
+
}
|
|
42
|
+
function routineIdToTemplateId(routineId) {
|
|
43
|
+
const name = routineId.startsWith('routine-') ? routineId.slice('routine-'.length) : routineId;
|
|
44
|
+
return `wr.templates.routine.${name}`;
|
|
37
45
|
}
|
|
38
46
|
function createRoutineExpander(routineId, definition) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
if (recursiveCheck.isErr())
|
|
42
|
-
return (0, neverthrow_1.err)(recursiveCheck.error);
|
|
43
|
-
for (const step of routineSteps) {
|
|
44
|
-
if (!step.id || !step.title) {
|
|
47
|
+
for (const step of definition.steps) {
|
|
48
|
+
if ('templateCall' in step && step.templateCall) {
|
|
45
49
|
return (0, neverthrow_1.err)({
|
|
46
50
|
code: 'TEMPLATE_EXPAND_FAILED',
|
|
47
|
-
templateId:
|
|
48
|
-
message: `Routine '${routineId}' step '${step.id
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
if (!step.prompt) {
|
|
52
|
-
return (0, neverthrow_1.err)({
|
|
53
|
-
code: 'TEMPLATE_EXPAND_FAILED',
|
|
54
|
-
templateId: `wr.templates.routine.${routineId}`,
|
|
55
|
-
message: `Routine '${routineId}' step '${step.id}' is missing required field 'prompt'.`,
|
|
51
|
+
templateId: routineIdToTemplateId(routineId),
|
|
52
|
+
message: `Routine '${routineId}' step '${step.id}' contains a templateCall. Recursive routine injection is not allowed.`,
|
|
56
53
|
});
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
|
-
const
|
|
56
|
+
const templateId = routineIdToTemplateId(routineId);
|
|
60
57
|
const expander = (callerId, args) => {
|
|
61
58
|
const expandedSteps = [];
|
|
62
|
-
for (const step of
|
|
63
|
-
|
|
59
|
+
for (const step of definition.steps) {
|
|
60
|
+
if (!step.id || !step.title) {
|
|
61
|
+
return (0, neverthrow_1.err)({
|
|
62
|
+
code: 'TEMPLATE_EXPAND_FAILED',
|
|
63
|
+
templateId,
|
|
64
|
+
message: `Routine '${routineId}' step '${step.id ?? '(missing id)'}' is missing required field '${!step.id ? 'id' : 'title'}'.`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (!step.prompt) {
|
|
68
|
+
return (0, neverthrow_1.err)({
|
|
69
|
+
code: 'TEMPLATE_EXPAND_FAILED',
|
|
70
|
+
templateId,
|
|
71
|
+
message: `Routine '${routineId}' step '${step.id}' is missing required field 'prompt'.`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const promptResult = substituteArgs(step.prompt, args, templateId, routineId, step.id);
|
|
64
75
|
if (promptResult.isErr())
|
|
65
76
|
return (0, neverthrow_1.err)(promptResult.error);
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
const titleResult = substituteArgs(step.title, args, templateId, routineId, step.id);
|
|
78
|
+
if (titleResult.isErr())
|
|
79
|
+
return (0, neverthrow_1.err)(titleResult.error);
|
|
69
80
|
const expandedStep = {
|
|
81
|
+
...step,
|
|
70
82
|
id: `${callerId}.${step.id}`,
|
|
71
|
-
title:
|
|
83
|
+
title: titleResult.value,
|
|
72
84
|
prompt: promptResult.value,
|
|
73
|
-
...(
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
...(definition.metaGuidance && definition.metaGuidance.length > 0
|
|
86
|
+
? {
|
|
87
|
+
guidance: [
|
|
88
|
+
...(step.guidance ?? []),
|
|
89
|
+
...definition.metaGuidance,
|
|
90
|
+
],
|
|
91
|
+
}
|
|
92
|
+
: {}),
|
|
76
93
|
};
|
|
77
94
|
expandedSteps.push(expandedStep);
|
|
78
95
|
}
|
|
@@ -80,26 +97,18 @@ function createRoutineExpander(routineId, definition) {
|
|
|
80
97
|
};
|
|
81
98
|
return (0, neverthrow_1.ok)(expander);
|
|
82
99
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
const allTemplates = new Map(STATIC_TEMPLATE_DEFINITIONS);
|
|
90
|
-
if (routineDefinitions) {
|
|
91
|
-
for (const [routineId, definition] of routineDefinitions) {
|
|
92
|
-
const templateId = routineIdToTemplateId(routineId);
|
|
93
|
-
const expanderResult = createRoutineExpander(routineId, definition);
|
|
94
|
-
if (expanderResult.isOk()) {
|
|
95
|
-
allTemplates.set(templateId, expanderResult.value);
|
|
96
|
-
}
|
|
100
|
+
const TEMPLATE_DEFINITIONS = new Map();
|
|
101
|
+
function createTemplateRegistry(routineExpanders) {
|
|
102
|
+
const allExpanders = new Map(TEMPLATE_DEFINITIONS);
|
|
103
|
+
if (routineExpanders) {
|
|
104
|
+
for (const [id, expander] of routineExpanders) {
|
|
105
|
+
allExpanders.set(id, expander);
|
|
97
106
|
}
|
|
98
107
|
}
|
|
99
|
-
const knownIds = [...
|
|
108
|
+
const knownIds = [...allExpanders.keys()];
|
|
100
109
|
return {
|
|
101
110
|
resolve(templateId) {
|
|
102
|
-
const expander =
|
|
111
|
+
const expander = allExpanders.get(templateId);
|
|
103
112
|
if (!expander) {
|
|
104
113
|
return (0, neverthrow_1.err)({
|
|
105
114
|
code: 'UNKNOWN_TEMPLATE',
|
|
@@ -110,7 +119,7 @@ function createTemplateRegistry(routineDefinitions) {
|
|
|
110
119
|
return (0, neverthrow_1.ok)(expander);
|
|
111
120
|
},
|
|
112
121
|
has(templateId) {
|
|
113
|
-
return
|
|
122
|
+
return allExpanders.has(templateId);
|
|
114
123
|
},
|
|
115
124
|
knownIds() {
|
|
116
125
|
return knownIds;
|
|
@@ -2,7 +2,6 @@ import { Workflow, WorkflowStepDefinition, LoopStepDefinition } from '../../type
|
|
|
2
2
|
import type { LoopConditionSource } from '../../types/workflow-definition';
|
|
3
3
|
import type { Result } from 'neverthrow';
|
|
4
4
|
import { type DomainError } from '../../domain/execution/error';
|
|
5
|
-
import { type TemplateRegistry } from './compiler/template-registry';
|
|
6
5
|
export interface CompiledLoop {
|
|
7
6
|
readonly loop: LoopStepDefinition;
|
|
8
7
|
readonly bodySteps: readonly WorkflowStepDefinition[];
|
|
@@ -15,11 +14,8 @@ export interface CompiledWorkflow {
|
|
|
15
14
|
readonly compiledLoops: ReadonlyMap<string, CompiledLoop>;
|
|
16
15
|
readonly loopBodyStepIds: ReadonlySet<string>;
|
|
17
16
|
}
|
|
18
|
-
export declare function resolveDefinitionSteps(steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[], features: readonly string[]
|
|
17
|
+
export declare function resolveDefinitionSteps(steps: readonly (WorkflowStepDefinition | LoopStepDefinition)[], features: readonly string[]): Result<readonly (WorkflowStepDefinition | LoopStepDefinition)[], DomainError>;
|
|
19
18
|
export declare class WorkflowCompiler {
|
|
20
|
-
private readonly templateRegistry;
|
|
21
|
-
constructor();
|
|
22
|
-
static withTemplateRegistry(registry: TemplateRegistry): WorkflowCompiler;
|
|
23
19
|
compile(workflow: Workflow): Result<CompiledWorkflow, DomainError>;
|
|
24
20
|
private deriveConditionSource;
|
|
25
21
|
private resolveLoopBody;
|
|
@@ -5,10 +5,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var WorkflowCompiler_1;
|
|
12
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
9
|
exports.WorkflowCompiler = void 0;
|
|
14
10
|
exports.resolveDefinitionSteps = resolveDefinitionSteps;
|
|
@@ -24,16 +20,47 @@ const resolve_features_1 = require("./compiler/resolve-features");
|
|
|
24
20
|
const feature_registry_1 = require("./compiler/feature-registry");
|
|
25
21
|
const resolve_templates_1 = require("./compiler/resolve-templates");
|
|
26
22
|
const template_registry_1 = require("./compiler/template-registry");
|
|
23
|
+
const routine_loader_1 = require("./compiler/routine-loader");
|
|
27
24
|
const _refRegistry = (0, ref_registry_1.createRefRegistry)();
|
|
28
25
|
const _featureRegistry = (0, feature_registry_1.createFeatureRegistry)();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
26
|
+
function buildTemplateRegistry() {
|
|
27
|
+
const routineExpanders = new Map();
|
|
28
|
+
const loadResult = (0, routine_loader_1.loadRoutineDefinitions)();
|
|
29
|
+
if (loadResult.isErr()) {
|
|
30
|
+
console.warn(`[WorkflowCompiler] Failed to load routine definitions: ${loadResult.error}`);
|
|
31
|
+
return (0, template_registry_1.createTemplateRegistry)();
|
|
32
|
+
}
|
|
33
|
+
const { routines, warnings } = loadResult.value;
|
|
34
|
+
for (const w of warnings) {
|
|
35
|
+
console.warn(`[WorkflowCompiler] Skipped routine file '${w.file}': ${w.reason}`);
|
|
36
|
+
}
|
|
37
|
+
for (const [routineId, definition] of routines) {
|
|
38
|
+
const expanderResult = (0, template_registry_1.createRoutineExpander)(routineId, definition);
|
|
39
|
+
if (expanderResult.isOk()) {
|
|
40
|
+
routineExpanders.set((0, template_registry_1.routineIdToTemplateId)(routineId), expanderResult.value);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.warn(`[WorkflowCompiler] Failed to create expander for routine '${routineId}': ${expanderResult.error.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return (0, template_registry_1.createTemplateRegistry)(routineExpanders.size > 0 ? routineExpanders : undefined);
|
|
47
|
+
}
|
|
48
|
+
let _templateRegistryCache;
|
|
49
|
+
function getTemplateRegistry() {
|
|
50
|
+
if (!_templateRegistryCache) {
|
|
51
|
+
_templateRegistryCache = buildTemplateRegistry();
|
|
52
|
+
}
|
|
53
|
+
return _templateRegistryCache;
|
|
54
|
+
}
|
|
55
|
+
function resolveDefinitionSteps(steps, features) {
|
|
56
|
+
const templatesResult = (0, resolve_templates_1.resolveTemplatesPass)(steps, getTemplateRegistry());
|
|
32
57
|
if (templatesResult.isErr()) {
|
|
33
58
|
const e = templatesResult.error;
|
|
34
59
|
const message = e.code === 'TEMPLATE_RESOLVE_ERROR'
|
|
35
60
|
? `Step '${e.stepId}': template error — ${e.cause.message}`
|
|
36
|
-
:
|
|
61
|
+
: e.code === 'DUPLICATE_STEP_ID'
|
|
62
|
+
? e.message
|
|
63
|
+
: `Step '${e.stepId}': template expansion error — ${e.cause.message}`;
|
|
37
64
|
return (0, neverthrow_1.err)(error_1.Err.invalidState(message));
|
|
38
65
|
}
|
|
39
66
|
const featuresResult = (0, resolve_features_1.resolveFeaturesPass)(templatesResult.value, features, _featureRegistry);
|
|
@@ -59,17 +86,9 @@ function resolveDefinitionSteps(steps, features, templateRegistry = _defaultTemp
|
|
|
59
86
|
}
|
|
60
87
|
return (0, neverthrow_1.ok)(blocksResult.value);
|
|
61
88
|
}
|
|
62
|
-
let WorkflowCompiler =
|
|
63
|
-
constructor() {
|
|
64
|
-
this.templateRegistry = _defaultTemplateRegistry;
|
|
65
|
-
}
|
|
66
|
-
static withTemplateRegistry(registry) {
|
|
67
|
-
const compiler = new WorkflowCompiler_1();
|
|
68
|
-
compiler.templateRegistry = registry;
|
|
69
|
-
return compiler;
|
|
70
|
-
}
|
|
89
|
+
let WorkflowCompiler = class WorkflowCompiler {
|
|
71
90
|
compile(workflow) {
|
|
72
|
-
const resolvedResult = resolveDefinitionSteps(workflow.definition.steps, workflow.definition.features ?? []
|
|
91
|
+
const resolvedResult = resolveDefinitionSteps(workflow.definition.steps, workflow.definition.features ?? []);
|
|
73
92
|
if (resolvedResult.isErr())
|
|
74
93
|
return (0, neverthrow_1.err)(resolvedResult.error);
|
|
75
94
|
const steps = resolvedResult.value;
|
|
@@ -170,7 +189,6 @@ let WorkflowCompiler = WorkflowCompiler_1 = class WorkflowCompiler {
|
|
|
170
189
|
}
|
|
171
190
|
};
|
|
172
191
|
exports.WorkflowCompiler = WorkflowCompiler;
|
|
173
|
-
exports.WorkflowCompiler = WorkflowCompiler =
|
|
174
|
-
(0, tsyringe_1.singleton)()
|
|
175
|
-
__metadata("design:paramtypes", [])
|
|
192
|
+
exports.WorkflowCompiler = WorkflowCompiler = __decorate([
|
|
193
|
+
(0, tsyringe_1.singleton)()
|
|
176
194
|
], WorkflowCompiler);
|
|
@@ -26,7 +26,6 @@ export declare class FileWorkflowStorage implements IWorkflowStorage {
|
|
|
26
26
|
private loadDefinitionFromFile;
|
|
27
27
|
loadAllWorkflows(): Promise<readonly Workflow[]>;
|
|
28
28
|
getWorkflowById(id: string): Promise<Workflow | null>;
|
|
29
|
-
getRoutineDefinitions(): Promise<ReadonlyMap<string, WorkflowDefinition>>;
|
|
30
29
|
listWorkflowSummaries(): Promise<readonly WorkflowSummary[]>;
|
|
31
30
|
save(definition: WorkflowDefinition): Promise<void>;
|
|
32
31
|
}
|
|
@@ -164,16 +164,6 @@ class FileWorkflowStorage {
|
|
|
164
164
|
}
|
|
165
165
|
return workflow;
|
|
166
166
|
}
|
|
167
|
-
async getRoutineDefinitions() {
|
|
168
|
-
const index = await this.getWorkflowIndex();
|
|
169
|
-
const routines = new Map();
|
|
170
|
-
for (const entry of index.values()) {
|
|
171
|
-
if (entry.id.startsWith('routine-')) {
|
|
172
|
-
routines.set(entry.id, entry.definition);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return routines;
|
|
176
|
-
}
|
|
177
167
|
async listWorkflowSummaries() {
|
|
178
168
|
const workflows = await this.loadAllWorkflows();
|
|
179
169
|
return workflows.map(workflow_1.toWorkflowSummary);
|
package/dist/manifest.json
CHANGED
|
@@ -42,20 +42,28 @@
|
|
|
42
42
|
"bytes": 4214
|
|
43
43
|
},
|
|
44
44
|
"application/services/compiler/resolve-templates.d.ts": {
|
|
45
|
-
"sha256": "
|
|
46
|
-
"bytes":
|
|
45
|
+
"sha256": "ccb4fbbf00f3ccbfcb9225998bcb9bfbd30a7e64dcfc2b2bc0f06701ee9a8e33",
|
|
46
|
+
"bytes": 905
|
|
47
47
|
},
|
|
48
48
|
"application/services/compiler/resolve-templates.js": {
|
|
49
|
-
"sha256": "
|
|
50
|
-
"bytes":
|
|
49
|
+
"sha256": "96f152a9f8b16aa7779b17ac56b555e45ad1213b32607961f060a080c6610a1a",
|
|
50
|
+
"bytes": 3590
|
|
51
|
+
},
|
|
52
|
+
"application/services/compiler/routine-loader.d.ts": {
|
|
53
|
+
"sha256": "831ed318f498c20ef9d547f38aea22a3fa2c677cd923634129545e7bf69ad241",
|
|
54
|
+
"bytes": 481
|
|
55
|
+
},
|
|
56
|
+
"application/services/compiler/routine-loader.js": {
|
|
57
|
+
"sha256": "fa5db5a759d88e63ef65bc877311d41003813d742fbdda7d06bc8abf56007988",
|
|
58
|
+
"bytes": 1849
|
|
51
59
|
},
|
|
52
60
|
"application/services/compiler/template-registry.d.ts": {
|
|
53
|
-
"sha256": "
|
|
54
|
-
"bytes":
|
|
61
|
+
"sha256": "5c8aef7bd75fce04d56985932eb2ad37e23c8bc87a77c75ee2addf018d3e619d",
|
|
62
|
+
"bytes": 1168
|
|
55
63
|
},
|
|
56
64
|
"application/services/compiler/template-registry.js": {
|
|
57
|
-
"sha256": "
|
|
58
|
-
"bytes":
|
|
65
|
+
"sha256": "92f824723e8d137533f8c752f2f753bb2a88e5812631de0a6428e3c6ff44543a",
|
|
66
|
+
"bytes": 5283
|
|
59
67
|
},
|
|
60
68
|
"application/services/enhanced-error-service.d.ts": {
|
|
61
69
|
"sha256": "b6fe8fad92717f0962f87aa9c0f88277bf28fe2b5e3cfd7875612ee57eb8c684",
|
|
@@ -98,12 +106,12 @@
|
|
|
98
106
|
"bytes": 32055
|
|
99
107
|
},
|
|
100
108
|
"application/services/workflow-compiler.d.ts": {
|
|
101
|
-
"sha256": "
|
|
102
|
-
"bytes":
|
|
109
|
+
"sha256": "41d0643ae2f07e5ce77a6e02344b5ca5b3c26bde828fbb307528a2ae097ac9d5",
|
|
110
|
+
"bytes": 1211
|
|
103
111
|
},
|
|
104
112
|
"application/services/workflow-compiler.js": {
|
|
105
|
-
"sha256": "
|
|
106
|
-
"bytes":
|
|
113
|
+
"sha256": "1adfbce7e79f827b1be530128dd9f4fc65bf427c851d7669fd11f20d4639631f",
|
|
114
|
+
"bytes": 9578
|
|
107
115
|
},
|
|
108
116
|
"application/services/workflow-interpreter.d.ts": {
|
|
109
117
|
"sha256": "56b5b5ad06d42096deba9f0abe7642c18a355a1e598749aab1730df4e9847674",
|
|
@@ -546,12 +554,12 @@
|
|
|
546
554
|
"bytes": 17854
|
|
547
555
|
},
|
|
548
556
|
"infrastructure/storage/file-workflow-storage.d.ts": {
|
|
549
|
-
"sha256": "
|
|
550
|
-
"bytes":
|
|
557
|
+
"sha256": "3ddd5c692ebec52dc36cf96013db510a8b0eb77aaeeb7ba38b646a69083f002a",
|
|
558
|
+
"bytes": 1428
|
|
551
559
|
},
|
|
552
560
|
"infrastructure/storage/file-workflow-storage.js": {
|
|
553
|
-
"sha256": "
|
|
554
|
-
"bytes":
|
|
561
|
+
"sha256": "e5cfd6f8a7fed72961e041aa9c92765c28f4b9872c16e7cbdbd533f0177ea0f0",
|
|
562
|
+
"bytes": 8562
|
|
555
563
|
},
|
|
556
564
|
"infrastructure/storage/git-workflow-storage.d.ts": {
|
|
557
565
|
"sha256": "67d7f10e12c78c674ced83da378cd159465d4b09176d8dfca240864b0d6f38c2",
|
package/package.json
CHANGED
|
@@ -40,13 +40,51 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
|
-
"id": "phase-
|
|
44
|
-
"title": "Phase
|
|
43
|
+
"id": "phase-1a-hypothesis",
|
|
44
|
+
"title": "Phase 1a: State Hypothesis",
|
|
45
45
|
"runCondition": {
|
|
46
46
|
"var": "taskComplexity",
|
|
47
47
|
"not_equals": "Small"
|
|
48
48
|
},
|
|
49
|
-
"prompt": "
|
|
49
|
+
"prompt": "Before any design work, state your current hypothesis in 3-5 sentences.\n\nBased on what you learned in Phase 0, write:\n1. Your current best guess for the approach\n2. Your main concern about that guess\n3. What would most likely make that guess wrong\n\nThis is your reference point. After design generation, you will compare the result against this hypothesis and say what changed your mind or what held firm.\n\nSet context variable:\n- `initialHypothesis`",
|
|
50
|
+
"requireConfirmation": false
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "phase-1b-design-quick",
|
|
54
|
+
"title": "Phase 1b: Lightweight Design (QUICK)",
|
|
55
|
+
"runCondition": {
|
|
56
|
+
"and": [
|
|
57
|
+
{ "var": "taskComplexity", "not_equals": "Small" },
|
|
58
|
+
{ "var": "rigorMode", "equals": "QUICK" }
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"prompt": "Generate a lightweight design inline. QUICK rigor means the path is clear and risk is low.\n\nProduce two mandatory candidates:\n1. The simplest possible change that satisfies acceptance criteria\n2. Follow the existing repo pattern for this kind of change\n\nFor each candidate:\n- One-sentence summary\n- Key tradeoff\n- Failure mode to watch\n- Philosophy fit (name specific principles)\n\nCompare and recommend. If both converge on the same approach, say so honestly.\n\nWrite the output to `design-candidates.md` using the same structure as the deep design path:\n- Problem Understanding\n- Candidates (each with: summary, tradeoff, failure mode, philosophy fit)\n- Comparison and Recommendation\n\nSet context variable:\n- `designSummary` — one-paragraph summary of the recommendation and why",
|
|
62
|
+
"requireConfirmation": false
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "phase-1b-design-deep",
|
|
66
|
+
"title": "Phase 1b: Design Generation (Injected Routine — Tension-Driven Design)",
|
|
67
|
+
"runCondition": {
|
|
68
|
+
"and": [
|
|
69
|
+
{ "var": "taskComplexity", "not_equals": "Small" },
|
|
70
|
+
{ "var": "rigorMode", "not_equals": "QUICK" }
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"templateCall": {
|
|
74
|
+
"templateId": "wr.templates.routine.tension-driven-design",
|
|
75
|
+
"args": {
|
|
76
|
+
"deliverableName": "design-candidates.md"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "phase-1c-challenge-and-select",
|
|
82
|
+
"title": "Phase 1c: Challenge and Select",
|
|
83
|
+
"runCondition": {
|
|
84
|
+
"var": "taskComplexity",
|
|
85
|
+
"not_equals": "Small"
|
|
86
|
+
},
|
|
87
|
+
"prompt": "Read `design-candidates.md`, compare against your initial hypothesis, and make the final architecture decision.\n\nInput contract: both QUICK and deep design paths produce `design-candidates.md` with candidates, tradeoffs, and a recommendation. Use that artifact as your primary input.\n\nPart A — Compare to hypothesis:\nRevisit `initialHypothesis`. Now that you have design candidates:\n- Where did the design work confirm your hypothesis?\n- Where did it challenge or change your thinking?\n- What did you learn that you hadn't considered?\nState explicitly what changed your mind and what held firm.\n\nPart B — Challenge the leading option:\n- What's the strongest argument against the recommended approach?\n- What assumption, if wrong, would invalidate it?\n- STANDARD/THOROUGH: optionally spawn ONE WorkRail Executor running `routine-hypothesis-challenge` focused on the leading option's failure modes\n- THOROUGH: optionally also spawn ONE WorkRail Executor running `routine-execution-simulation` to trace the 3 most likely failure scenarios\n\nPart C — Select:\nMake the final architecture decision. The design output is evidence, not a decision — you own the choice.\n\nIf the simplest solution satisfies acceptance criteria, prefer it. Complexity must justify itself. If the challenged leading candidate no longer looks best, switch deliberately rather than defending sunk cost.\n\nSet context variables:\n- `selectedApproach` — the chosen design with rationale tied back to tensions\n- `runnerUpApproach` — the next-best option and why it lost\n- `architectureRationale` — which tensions were resolved and which were accepted\n- `pivotTriggers` — specific conditions under which you'd switch to the runner-up\n- `keyRiskToMonitor` — the failure mode of the selected approach\n- `acceptedTradeoffs` — what the selected approach gives up (feeds directly into design review)\n- `identifiedFailureModes` — per-candidate failure modes (feeds directly into design review)",
|
|
50
88
|
"requireConfirmation": {
|
|
51
89
|
"or": [
|
|
52
90
|
{ "var": "automationLevel", "equals": "Low" },
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "routine-injection-example",
|
|
3
|
+
"name": "Routine Injection Example — Design with Tension-Driven Design",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Example workflow that uses templateCall to inject the tension-driven-design routine inline. Validates the end-to-end routine injection path.",
|
|
6
|
+
"steps": [
|
|
7
|
+
{
|
|
8
|
+
"id": "phase-0-gather",
|
|
9
|
+
"title": "Phase 0: Gather Context",
|
|
10
|
+
"prompt": "Gather context about the problem space."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "phase-1-design",
|
|
14
|
+
"title": "Phase 1: Design (Injected Routine)",
|
|
15
|
+
"templateCall": {
|
|
16
|
+
"templateId": "wr.templates.routine.tension-driven-design",
|
|
17
|
+
"args": {
|
|
18
|
+
"deliverableName": "design-candidates.md"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "phase-2-implement",
|
|
24
|
+
"title": "Phase 2: Implement",
|
|
25
|
+
"prompt": "Implement the selected design."
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|