@agentled/cli 0.6.4 → 0.6.6
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/commands/auth.js +18 -0
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/dryrun.d.ts +23 -0
- package/dist/commands/dryrun.js +641 -0
- package/dist/commands/dryrun.js.map +1 -0
- package/dist/commands/fixture.d.ts +10 -0
- package/dist/commands/fixture.js +155 -0
- package/dist/commands/fixture.js.map +1 -0
- package/dist/commands/group-manifest.d.ts +15 -0
- package/dist/commands/group-manifest.js +488 -0
- package/dist/commands/group-manifest.js.map +1 -0
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.js +83 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/onboarding.js +70 -3
- package/dist/commands/onboarding.js.map +1 -1
- package/dist/commands/test.d.ts +15 -0
- package/dist/commands/test.js +98 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/workflows.js +189 -4
- package/dist/commands/workflows.js.map +1 -1
- package/dist/commands/workspace.js +65 -0
- package/dist/commands/workspace.js.map +1 -1
- package/dist/context-schema.js +3 -4
- package/dist/context-schema.js.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/browser-auth.js +6 -2
- package/dist/utils/browser-auth.js.map +1 -1
- package/dist/utils/pipeline-lint.d.ts +20 -0
- package/dist/utils/pipeline-lint.js +348 -0
- package/dist/utils/pipeline-lint.js.map +1 -0
- package/dist/utils/preflight.js +1 -1
- package/dist/utils/test-runner.d.ts +85 -0
- package/dist/utils/test-runner.js +283 -0
- package/dist/utils/test-runner.js.map +1 -0
- package/dist/utils/workspace-folder.d.ts +46 -0
- package/dist/utils/workspace-folder.js +340 -0
- package/dist/utils/workspace-folder.js.map +1 -0
- package/package.json +1 -1
- package/patterns/v1/12-event-driven-workflow-groups.md +288 -0
- package/scaffolds/extract-threshold-alert.json +3 -3
- package/skills/agentled/SKILL.md +169 -38
- package/skills/agentled/WHY-AGENTLED.md +15 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace folder helpers — local agentled_<slug>/ directory used as the
|
|
3
|
+
* agent's working tree for building, testing, and iterating on workflows.
|
|
4
|
+
*
|
|
5
|
+
* Folder layout:
|
|
6
|
+
*
|
|
7
|
+
* agentled_<slug>/
|
|
8
|
+
* ├── README.md # agent-facing guide
|
|
9
|
+
* ├── .agentled/
|
|
10
|
+
* │ ├── workspace.json # workspaceId, slug, apiBase, syncedAt
|
|
11
|
+
* │ └── cache/
|
|
12
|
+
* │ ├── apps.json # app registry
|
|
13
|
+
* │ └── models.json # supported models
|
|
14
|
+
* ├── docs/
|
|
15
|
+
* │ ├── SKILL.md # full skill reference (copied from package)
|
|
16
|
+
* │ └── GOTCHAS.md # documented failure modes
|
|
17
|
+
* ├── examples/
|
|
18
|
+
* │ ├── scaffolds/ # bundled scaffold JSONs (editable)
|
|
19
|
+
* │ └── live/ # workflows pulled from the workspace
|
|
20
|
+
* ├── fixtures/
|
|
21
|
+
* │ └── step-outputs/ # captured step outputs (zero-credit replay)
|
|
22
|
+
* ├── tests/ # <wfId>.test.json declarative test files
|
|
23
|
+
* └── drafts/ # in-progress pipeline JSON files
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs';
|
|
26
|
+
import { dirname, join, resolve } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Resolve a workspace folder from cwd: try cwd → walk up → scan children
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
export function findWorkspaceDir(cwd = process.cwd()) {
|
|
32
|
+
// 1. cwd itself is a workspace
|
|
33
|
+
if (existsSync(join(cwd, '.agentled', 'workspace.json')))
|
|
34
|
+
return cwd;
|
|
35
|
+
// 2. Walk up parents
|
|
36
|
+
let current = cwd;
|
|
37
|
+
while (true) {
|
|
38
|
+
const parent = dirname(current);
|
|
39
|
+
if (parent === current)
|
|
40
|
+
break;
|
|
41
|
+
if (existsSync(join(parent, '.agentled', 'workspace.json')))
|
|
42
|
+
return parent;
|
|
43
|
+
current = parent;
|
|
44
|
+
}
|
|
45
|
+
// 3. Scan child dirs for agentled_*
|
|
46
|
+
try {
|
|
47
|
+
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory() || !entry.name.startsWith('agentled_'))
|
|
50
|
+
continue;
|
|
51
|
+
const wsFile = join(cwd, entry.name, '.agentled', 'workspace.json');
|
|
52
|
+
if (existsSync(wsFile))
|
|
53
|
+
return join(cwd, entry.name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// cwd unreadable
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
export function readWorkspaceMeta(wsDir) {
|
|
62
|
+
const raw = readFileSync(join(wsDir, '.agentled', 'workspace.json'), 'utf-8');
|
|
63
|
+
return JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
export function writeWorkspaceMeta(wsDir, meta) {
|
|
66
|
+
writeFileSync(join(wsDir, '.agentled', 'workspace.json'), JSON.stringify(meta, null, 2) + '\n');
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Resolve bundled assets shipped with @agentled/cli
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
function pkgRoot() {
|
|
72
|
+
const here = fileURLToPath(import.meta.url);
|
|
73
|
+
// dist/utils/workspace-folder.js → ../.. → package root
|
|
74
|
+
return resolve(dirname(here), '..', '..');
|
|
75
|
+
}
|
|
76
|
+
export function bundledScaffoldsDir() {
|
|
77
|
+
return join(pkgRoot(), 'scaffolds');
|
|
78
|
+
}
|
|
79
|
+
export function bundledSkillMd() {
|
|
80
|
+
return join(pkgRoot(), 'skills', 'agentled', 'SKILL.md');
|
|
81
|
+
}
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// GOTCHAS.md — embedded so it stays in sync with the CLI release
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
export const GOTCHAS_MD = `# Agentled — Documented Failure Modes (GOTCHAS)
|
|
86
|
+
|
|
87
|
+
These pass JSON syntax validation but silently misbehave at runtime.
|
|
88
|
+
Run \`agentled workflows lint <file>\` to catch them statically before deploying.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 1. \`criteria\` not \`conditions\` in entryConditions [CRITERIA_NOT_CONDITIONS]
|
|
93
|
+
|
|
94
|
+
**Wrong:** \`{ "entryConditions": { "conditions": [...] } }\`
|
|
95
|
+
**Correct:** \`{ "entryConditions": { "criteria": [...] } }\`
|
|
96
|
+
|
|
97
|
+
The executor reads \`entryConditions.criteria\`. The key \`conditions\` is silently ignored.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 2. \`variable\` not \`field\` in criteria items [VARIABLE_NOT_FIELD]
|
|
102
|
+
|
|
103
|
+
**Wrong:** \`{ "field": "{{steps.x.score}}", "operator": ">", "value": 70 }\`
|
|
104
|
+
**Correct:** \`{ "variable": "{{steps.x.score}}", "operator": ">", "value": 70 }\`
|
|
105
|
+
|
|
106
|
+
Using \`field\` causes the criterion to be silently skipped.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 3. Gmail label_id must be the internal Label_XXXX ID [GMAIL_LABEL_DISPLAY_NAME]
|
|
111
|
+
|
|
112
|
+
Gmail's API requires \`Label_XXXXXXXXXX\` IDs, not display names.
|
|
113
|
+
Add a \`GMAIL_CREATE_LABEL\` step before and use \`{{steps.ensure-label.id}}\`.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 4. \`aiActionWithTools\` with no tools [AI_STEP_TOOLS_REQUIRED]
|
|
118
|
+
|
|
119
|
+
A step with \`type: "aiActionWithTools"\` must have at least one tool in
|
|
120
|
+
\`step.tools\` or \`step.agent.tools\`.
|
|
121
|
+
|
|
122
|
+
Valid \`builtinType\` values: \`web_search\`, \`file_search\`, \`code_interpreter\`,
|
|
123
|
+
\`fetch_website_content\`, \`kg_search\`, \`kg_traverse\`, \`kg_nodes\`, \`kg_write\`,
|
|
124
|
+
\`workspace_memory\`.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 5. Email steps need type + approval action + outreachProfile [EMAIL_MISSING_*]
|
|
129
|
+
|
|
130
|
+
Three required pieces:
|
|
131
|
+
- \`pipelineStepPrompt.type: "email"\`
|
|
132
|
+
- \`onApproval.action: "schedule-email"\`
|
|
133
|
+
- \`outreachProfile\` input page in \`context.inputPages\`
|
|
134
|
+
|
|
135
|
+
Missing the schedule-email action means the email is drafted but never sent.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 6. Only the first step in a loop gets \`loopConfig\` [LOOP_CONFIG_MULTIPLE_STEPS]
|
|
140
|
+
|
|
141
|
+
\`loopConfig\` must be on the first step in the loop chain only. Subsequent
|
|
142
|
+
steps inside the loop iterate automatically.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 7. Don't pass raw \`{{input.*}}\` directly to search APIs [RAW_INPUT_TO_SEARCH]
|
|
147
|
+
|
|
148
|
+
Add an aiAction step before the search that generates optimized queries.
|
|
149
|
+
Raw user input makes poor search queries.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 8. Child workflows must use \`return\` step, not \`milestone\` [CHILD_WORKFLOW_NO_RETURN]
|
|
154
|
+
|
|
155
|
+
If a workflow is called via \`agentled.call-workflow\`, use \`type: "return"\`.
|
|
156
|
+
\`milestone\` produces no return data. Also set \`context.executionInputConfig.internal: true\`.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 9. Model IDs are internal format, not Anthropic format [MODEL_ID_FORMAT]
|
|
161
|
+
|
|
162
|
+
Wrong: \`claude-sonnet-4-6\`. Correct: \`claude-4-6-sonnet\`.
|
|
163
|
+
Run \`agentled models list\` for valid internal IDs.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 10. Native app actionId must include appId prefix [ACTION_ID_MISSING_PREFIX]
|
|
168
|
+
|
|
169
|
+
Wrong: \`{ "id": "kg", "actionId": "read-list" }\`
|
|
170
|
+
Correct: \`{ "id": "kg", "actionId": "kg.read-list" }\`
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 11. \`loop_completion\` criteria requires \`onCriteriaFail: "wait"\` [LOOP_COMPLETION_NOT_WAIT]
|
|
175
|
+
|
|
176
|
+
Without \`onCriteriaFail: "wait"\`, the step skips instead of blocking until
|
|
177
|
+
the loop finishes. The \`stepId\` field is also required.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 12. Arrays in JSON template strings — don't JSON.stringify
|
|
182
|
+
|
|
183
|
+
The serializer detects when a template variable is the sole content of a JSON
|
|
184
|
+
field and inlines the raw value. Pass \`{ "items": "{{steps.x.items}}" }\`
|
|
185
|
+
directly — no stringify needed.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 13. \`kg.upsert-rows\` needs \`userKey\` for dedup; \`kg.add-rows\` always inserts
|
|
190
|
+
|
|
191
|
+
\`kg.upsert-rows\` with \`userKey\`: same key = same row, cross-run dedup O(1).
|
|
192
|
+
\`kg.add-rows\`: always inserts a new row, duplicates accumulate.
|
|
193
|
+
Use \`mergeStrategy: "merge"\` to preserve downstream-added fields.
|
|
194
|
+
`;
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// README.md — agent-facing guide written into each new workspace folder
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
export function makeWorkspaceReadme(meta) {
|
|
199
|
+
return `# Agentled Workspace — ${meta.name}
|
|
200
|
+
|
|
201
|
+
**Workspace ID:** \`${meta.workspaceId}\`
|
|
202
|
+
**Slug:** \`${meta.slug}\`
|
|
203
|
+
**API Base:** ${meta.apiBase}
|
|
204
|
+
**Last synced:** ${meta.syncedAt}
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## What this folder is
|
|
209
|
+
|
|
210
|
+
Local working tree for building, testing, and iterating on workflows for the
|
|
211
|
+
\`${meta.slug}\` workspace.
|
|
212
|
+
|
|
213
|
+
- **\`docs/\`** — SKILL.md (full reference) + GOTCHAS.md (13 failure modes)
|
|
214
|
+
- **\`examples/scaffolds/\`** — preflight-clean pipeline JSON skeletons
|
|
215
|
+
- **\`examples/live/\`** — pulled copies of real workspace workflows
|
|
216
|
+
- **\`fixtures/step-outputs/\`** — captured step outputs for credit-free testing
|
|
217
|
+
- **\`tests/\`** — declarative test files for each workflow
|
|
218
|
+
- **\`drafts/\`** — in-progress pipelines before pushing to workspace
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Quick commands
|
|
223
|
+
|
|
224
|
+
\`\`\`bash
|
|
225
|
+
# Pull a workflow from the workspace to iterate locally
|
|
226
|
+
agentled workflows pull <workflowId>
|
|
227
|
+
|
|
228
|
+
# Static gotcha checks (no API)
|
|
229
|
+
agentled workflows lint <file.json>
|
|
230
|
+
|
|
231
|
+
# Capture step outputs from a past execution
|
|
232
|
+
agentled fixture capture <executionId> --wf <workflowId>
|
|
233
|
+
|
|
234
|
+
# Run declarative tests against captured fixtures (zero credits)
|
|
235
|
+
agentled test <workflowId>
|
|
236
|
+
|
|
237
|
+
# Run tests with live API calls (costs credits)
|
|
238
|
+
agentled test <workflowId> --live
|
|
239
|
+
|
|
240
|
+
# Push a local pipeline to the workspace
|
|
241
|
+
agentled workflows create --file <file.json>
|
|
242
|
+
|
|
243
|
+
# Refresh cached app registry + models
|
|
244
|
+
agentled workspace sync
|
|
245
|
+
\`\`\`
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Testing strategy (zero-credit loop)
|
|
250
|
+
|
|
251
|
+
1. Build the workflow incrementally via MCP tools or CLI.
|
|
252
|
+
2. Run once via \`workflows start\` with sample input.
|
|
253
|
+
3. Capture outputs: \`agentled fixture capture <execId> --wf <wfId>\`
|
|
254
|
+
4. Iterate on step logic, test against fixtures: \`agentled test <wfId>\`
|
|
255
|
+
5. For AI step changes, re-test with \`--live\` (costs credits for that step only).
|
|
256
|
+
6. Use \`agentled workflows lint\` before every \`workflows validate\` to catch gotchas first.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Folder layout
|
|
261
|
+
|
|
262
|
+
\`\`\`
|
|
263
|
+
agentled_${meta.slug}/
|
|
264
|
+
├── README.md
|
|
265
|
+
├── .agentled/
|
|
266
|
+
│ ├── workspace.json
|
|
267
|
+
│ └── cache/
|
|
268
|
+
│ ├── apps.json
|
|
269
|
+
│ └── models.json
|
|
270
|
+
├── docs/
|
|
271
|
+
│ ├── SKILL.md
|
|
272
|
+
│ └── GOTCHAS.md
|
|
273
|
+
├── examples/
|
|
274
|
+
│ ├── scaffolds/
|
|
275
|
+
│ └── live/
|
|
276
|
+
├── fixtures/
|
|
277
|
+
│ └── step-outputs/
|
|
278
|
+
├── tests/
|
|
279
|
+
└── drafts/
|
|
280
|
+
\`\`\`
|
|
281
|
+
`;
|
|
282
|
+
}
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Folder bootstrap: create dirs, copy bundled assets, write GOTCHAS+README
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
export function createWorkspaceFolder(folderPath, meta, opts = {}) {
|
|
287
|
+
const dirs = [
|
|
288
|
+
folderPath,
|
|
289
|
+
join(folderPath, '.agentled', 'cache'),
|
|
290
|
+
join(folderPath, 'docs'),
|
|
291
|
+
join(folderPath, 'examples', 'scaffolds'),
|
|
292
|
+
join(folderPath, 'examples', 'live'),
|
|
293
|
+
join(folderPath, 'fixtures', 'step-outputs'),
|
|
294
|
+
join(folderPath, 'tests'),
|
|
295
|
+
join(folderPath, 'drafts'),
|
|
296
|
+
];
|
|
297
|
+
for (const dir of dirs)
|
|
298
|
+
mkdirSync(dir, { recursive: true });
|
|
299
|
+
writeWorkspaceMeta(folderPath, meta);
|
|
300
|
+
if (opts.appsList) {
|
|
301
|
+
writeFileSync(join(folderPath, '.agentled', 'cache', 'apps.json'), JSON.stringify(opts.appsList, null, 2) + '\n');
|
|
302
|
+
}
|
|
303
|
+
if (opts.modelsList) {
|
|
304
|
+
writeFileSync(join(folderPath, '.agentled', 'cache', 'models.json'), JSON.stringify(opts.modelsList, null, 2) + '\n');
|
|
305
|
+
}
|
|
306
|
+
// Copy SKILL.md from bundled package
|
|
307
|
+
const skillSrc = bundledSkillMd();
|
|
308
|
+
if (existsSync(skillSrc)) {
|
|
309
|
+
copyFileSync(skillSrc, join(folderPath, 'docs', 'SKILL.md'));
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
writeFileSync(join(folderPath, 'docs', 'SKILL.md'), '# SKILL.md\n\nRun `agentled workspace sync` to refresh.\n');
|
|
313
|
+
}
|
|
314
|
+
writeFileSync(join(folderPath, 'docs', 'GOTCHAS.md'), GOTCHAS_MD);
|
|
315
|
+
// Copy bundled scaffolds into examples/scaffolds/
|
|
316
|
+
const scaffoldDir = bundledScaffoldsDir();
|
|
317
|
+
if (existsSync(scaffoldDir)) {
|
|
318
|
+
for (const file of readdirSync(scaffoldDir).filter((f) => f.endsWith('.json'))) {
|
|
319
|
+
copyFileSync(join(scaffoldDir, file), join(folderPath, 'examples', 'scaffolds', file));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
writeFileSync(join(folderPath, 'README.md'), makeWorkspaceReadme(meta));
|
|
323
|
+
writeFileSync(join(folderPath, '.gitignore'), ['.agentled/cache/', 'fixtures/', 'tests/_runs/'].join('\n') + '\n');
|
|
324
|
+
}
|
|
325
|
+
export function refreshWorkspaceFolder(wsDir, opts) {
|
|
326
|
+
const cacheDir = join(wsDir, '.agentled', 'cache');
|
|
327
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
328
|
+
if (opts.appsList)
|
|
329
|
+
writeFileSync(join(cacheDir, 'apps.json'), JSON.stringify(opts.appsList, null, 2) + '\n');
|
|
330
|
+
if (opts.modelsList)
|
|
331
|
+
writeFileSync(join(cacheDir, 'models.json'), JSON.stringify(opts.modelsList, null, 2) + '\n');
|
|
332
|
+
const skillSrc = bundledSkillMd();
|
|
333
|
+
if (existsSync(skillSrc))
|
|
334
|
+
copyFileSync(skillSrc, join(wsDir, 'docs', 'SKILL.md'));
|
|
335
|
+
writeFileSync(join(wsDir, 'docs', 'GOTCHAS.md'), GOTCHAS_MD);
|
|
336
|
+
const meta = readWorkspaceMeta(wsDir);
|
|
337
|
+
meta.syncedAt = new Date().toISOString();
|
|
338
|
+
writeWorkspaceMeta(wsDir, meta);
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=workspace-folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-folder.js","sourceRoot":"","sources":["../../src/utils/workspace-folder.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# Pattern 12 — Event-Driven Workflow Groups
|
|
2
|
+
|
|
3
|
+
Multi-workflow systems connected through shared KG lists and status transitions.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use this pattern whenever the user's business process naturally decomposes into **more than one workflow** connected by shared state. Common signals:
|
|
8
|
+
|
|
9
|
+
- "Find leads, then process them on a schedule"
|
|
10
|
+
- "Source from multiple places into one canonical list"
|
|
11
|
+
- "Trigger downstream outreach when an upstream entity reaches a milestone"
|
|
12
|
+
- "Process inbound items, triage them, then approve a response"
|
|
13
|
+
- "Run sourcing weekly but scoring only when new items arrive"
|
|
14
|
+
|
|
15
|
+
A single monolithic workflow is only appropriate for truly one-shot, linear processes. Most real operations are a group.
|
|
16
|
+
|
|
17
|
+
## Core insight: workflows communicate through KG status, not direct calls
|
|
18
|
+
|
|
19
|
+
Instead of one large workflow that does everything, build workflows that each own one stage of a process and hand off to the next via KG row status transitions.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
[Workflow A] trigger → source entities → kg.upsert-rows(status: "new") → done
|
|
23
|
+
[Workflow B] trigger → kg.read-list(filter: "new") → enrich/score → kg.update-rows(status: "scored") → done
|
|
24
|
+
[Workflow C] trigger → kg.read-list(filter: "scored") → outreach → kg.update-rows(status: "outreached") → done
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Each workflow is independently runnable, retryable, and testable. No direct dependency between them at runtime — only the KG list is shared.
|
|
28
|
+
|
|
29
|
+
## The Group Manifest
|
|
30
|
+
|
|
31
|
+
Before creating any workflow, write a group manifest that describes the whole system. This keeps design state durable and lets you validate the plan locally before touching the API.
|
|
32
|
+
|
|
33
|
+
The example below is a generic two-list, seven-workflow group: continuous founder sourcing → self-serve paid booking → per-edition activation. The structure is template-shaped — fill in your group's identity and replace the workflows, lists, and states with your domain's vocabulary.
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"$schema": "https://schemas.agentled.app/group-manifest/v0.json",
|
|
38
|
+
"manifestVersion": "0.1.0",
|
|
39
|
+
"group": {
|
|
40
|
+
"id": "founder-showcase",
|
|
41
|
+
"name": "Founder Showcase Events",
|
|
42
|
+
"description": "Continuous founder sourcing → self-serve paid booking → per-edition investor activation.",
|
|
43
|
+
"goal": "Optimize for funnel conversion, not theme curation."
|
|
44
|
+
},
|
|
45
|
+
"operatingPrinciples": [
|
|
46
|
+
"Do not pick themes upfront — the first paid founder sets the edition theme and date.",
|
|
47
|
+
"Outreach is broad and continuous, not one batch per edition.",
|
|
48
|
+
"Investor outreach fires per edition, triggered by the first confirmed paid founder.",
|
|
49
|
+
"All customer-facing messages are approval-gated."
|
|
50
|
+
],
|
|
51
|
+
"knowledgeLists": {
|
|
52
|
+
"founders": {
|
|
53
|
+
"description": "Founders sourced and tracked through the showcase funnel.",
|
|
54
|
+
"userKeyField": "linkedin_url",
|
|
55
|
+
"fields": [
|
|
56
|
+
{ "name": "name", "type": "string" },
|
|
57
|
+
{ "name": "linkedin_url", "type": "string" },
|
|
58
|
+
{ "name": "domain", "type": "string" },
|
|
59
|
+
{ "name": "email", "type": "string" },
|
|
60
|
+
{ "name": "score", "type": "number" },
|
|
61
|
+
{ "name": "edition_id", "type": "string" },
|
|
62
|
+
{ "name": "status", "type": "string" }
|
|
63
|
+
],
|
|
64
|
+
"producers": ["source-channel-a", "source-channel-b"],
|
|
65
|
+
"consumers": ["enrich-and-score", "send-founder-outreach"]
|
|
66
|
+
},
|
|
67
|
+
"editions": {
|
|
68
|
+
"description": "Showcase editions, each triggered by the first paid founder confirmation.",
|
|
69
|
+
"userKeyField": "edition_id",
|
|
70
|
+
"fields": [
|
|
71
|
+
{ "name": "edition_id", "type": "string" },
|
|
72
|
+
{ "name": "theme", "type": "string" },
|
|
73
|
+
{ "name": "date", "type": "string" },
|
|
74
|
+
{ "name": "confirmed_founders", "type": "number" },
|
|
75
|
+
{ "name": "status", "type": "string" }
|
|
76
|
+
],
|
|
77
|
+
"producers": ["process-bookings"],
|
|
78
|
+
"consumers": ["activate-investors", "edition-report"]
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"stateMachines": {
|
|
82
|
+
"founders": {
|
|
83
|
+
"states": ["new", "enriched", "scored", "outreached", "booked", "confirmed", "rejected"],
|
|
84
|
+
"initialState": "new",
|
|
85
|
+
"transitions": [
|
|
86
|
+
{ "from": "new", "to": "enriched", "trigger": "enrich-and-score" },
|
|
87
|
+
{ "from": "enriched", "to": "scored", "trigger": "enrich-and-score" },
|
|
88
|
+
{ "from": "scored", "to": "outreached", "trigger": "send-founder-outreach" },
|
|
89
|
+
{ "from": "outreached", "to": "booked", "trigger": "process-bookings", "description": "Stripe checkout initiated" },
|
|
90
|
+
{ "from": "booked", "to": "confirmed", "trigger": "process-bookings", "description": "Payment confirmed" },
|
|
91
|
+
{ "from": "scored", "to": "rejected", "trigger": "enrich-and-score", "description": "Score below threshold" }
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"editions": {
|
|
95
|
+
"states": ["forming", "active", "completed"],
|
|
96
|
+
"initialState": "forming",
|
|
97
|
+
"transitions": [
|
|
98
|
+
{ "from": "forming", "to": "active", "trigger": "process-bookings", "description": "First paid founder confirmed" },
|
|
99
|
+
{ "from": "active", "to": "completed", "trigger": "edition-report" }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"workflows": [
|
|
104
|
+
{
|
|
105
|
+
"id": "source-channel-a",
|
|
106
|
+
"name": "Source — Channel A",
|
|
107
|
+
"purpose": "Find recently active founders from channel A and add to KG with status: new.",
|
|
108
|
+
"trigger": "schedule",
|
|
109
|
+
"schedule": "weekly",
|
|
110
|
+
"writes": ["founders"],
|
|
111
|
+
"creditEstimate": "5-10",
|
|
112
|
+
"status": "planned"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "source-channel-b",
|
|
116
|
+
"name": "Source — Channel B",
|
|
117
|
+
"purpose": "Find founders from channel B and add to KG with status: new.",
|
|
118
|
+
"trigger": "schedule",
|
|
119
|
+
"schedule": "weekly",
|
|
120
|
+
"writes": ["founders"],
|
|
121
|
+
"creditEstimate": "5-10",
|
|
122
|
+
"status": "planned"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "enrich-and-score",
|
|
126
|
+
"name": "Enrich & Score",
|
|
127
|
+
"purpose": "Enrich new founders via LinkedIn, find contact emails, score against ICP. Update status to scored or rejected.",
|
|
128
|
+
"trigger": "schedule",
|
|
129
|
+
"schedule": "daily",
|
|
130
|
+
"reads": ["founders"],
|
|
131
|
+
"writes": ["founders"],
|
|
132
|
+
"creditEstimate": "15-40",
|
|
133
|
+
"status": "planned"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": "send-founder-outreach",
|
|
137
|
+
"name": "Send Founder Outreach",
|
|
138
|
+
"purpose": "Draft personalized outreach to scored founders about the showcase opportunity. Send after approval.",
|
|
139
|
+
"trigger": "schedule",
|
|
140
|
+
"schedule": "weekly",
|
|
141
|
+
"reads": ["founders"],
|
|
142
|
+
"writes": ["founders"],
|
|
143
|
+
"approvalGates": ["draft-outreach-email"],
|
|
144
|
+
"creditEstimate": "5-15",
|
|
145
|
+
"status": "planned"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"id": "process-bookings",
|
|
149
|
+
"name": "Process Bookings",
|
|
150
|
+
"purpose": "Receive Stripe webhook or booking form submission, update founder status to booked/confirmed, and create/update edition when first founder confirms.",
|
|
151
|
+
"trigger": "webhook",
|
|
152
|
+
"reads": ["founders"],
|
|
153
|
+
"writes": ["founders", "editions"],
|
|
154
|
+
"creditEstimate": "2-5",
|
|
155
|
+
"status": "planned"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"id": "activate-investors",
|
|
159
|
+
"name": "Activate Investors",
|
|
160
|
+
"purpose": "When a new edition activates (first confirmed founder), reach out to investors for that edition's theme. Send after approval.",
|
|
161
|
+
"trigger": "schedule",
|
|
162
|
+
"schedule": "daily",
|
|
163
|
+
"reads": ["editions"],
|
|
164
|
+
"approvalGates": ["draft-investor-email"],
|
|
165
|
+
"creditEstimate": "5-15",
|
|
166
|
+
"status": "planned"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "edition-report",
|
|
170
|
+
"name": "Edition Report",
|
|
171
|
+
"purpose": "Generate a post-event report for each completed edition: attendees, scores, conversion rate.",
|
|
172
|
+
"trigger": "schedule",
|
|
173
|
+
"reads": ["founders", "editions"],
|
|
174
|
+
"writes": ["editions"],
|
|
175
|
+
"creditEstimate": "5-10",
|
|
176
|
+
"status": "planned"
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"existingWorkflows": [
|
|
180
|
+
{
|
|
181
|
+
"workflowId": "<existing-source-workflow-id>",
|
|
182
|
+
"name": "Existing source workflow",
|
|
183
|
+
"action": "reuse",
|
|
184
|
+
"notes": "Already sources entities. Will be wired to write to the founders KG list with status: new."
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
"integrations": {
|
|
188
|
+
"agentled": { "required": true, "purpose": "LinkedIn enrichment" },
|
|
189
|
+
"hunter": { "required": true, "purpose": "Email finding" },
|
|
190
|
+
"kg": { "required": true, "purpose": "KG list reads/writes for founders and editions" },
|
|
191
|
+
"webhook": { "required": true, "purpose": "Stripe booking webhooks" },
|
|
192
|
+
"gmail": { "required": true, "purpose": "Outreach email sending" }
|
|
193
|
+
},
|
|
194
|
+
"workspaceSettings": [
|
|
195
|
+
"Outreach profile configured with sender name and connected Gmail account",
|
|
196
|
+
"Stripe webhook URL registered and pointing to process-bookings trigger"
|
|
197
|
+
],
|
|
198
|
+
"buildOrder": [
|
|
199
|
+
"source-channel-a",
|
|
200
|
+
"source-channel-b",
|
|
201
|
+
"enrich-and-score",
|
|
202
|
+
"send-founder-outreach",
|
|
203
|
+
"process-bookings",
|
|
204
|
+
"activate-investors",
|
|
205
|
+
"edition-report"
|
|
206
|
+
],
|
|
207
|
+
"openQuestions": [
|
|
208
|
+
{
|
|
209
|
+
"question": "What is the primary success metric: paid bookings per month, qualified entities contacted, or investor confirmations per edition?",
|
|
210
|
+
"impact": "Determines which KPI to surface in the edition report and which step outputs to track in analytics.",
|
|
211
|
+
"status": "open"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"question": "What is the system of record for bookings: Stripe webhook, a form provider, or a manual update?",
|
|
215
|
+
"impact": "Determines trigger type and input schema of process-bookings workflow.",
|
|
216
|
+
"status": "open"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"question": "How many entities constitute a full edition, and what is the minimum to activate downstream outreach?",
|
|
220
|
+
"impact": "Entry condition logic in activate-investors and edition creation in process-bookings.",
|
|
221
|
+
"status": "open"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## CLI commands
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# Scaffold a starter manifest for your pattern
|
|
231
|
+
agentled group-manifest scaffold event-driven-funnel --out workflow-groups/<group-slug>/group.manifest.json
|
|
232
|
+
|
|
233
|
+
# Edit the manifest to match your actual workspace
|
|
234
|
+
|
|
235
|
+
# Validate locally before touching the API
|
|
236
|
+
agentled group-manifest validate workflow-groups/<group-slug>/group.manifest.json
|
|
237
|
+
|
|
238
|
+
# One-shot workspace orientation (run at session start)
|
|
239
|
+
agentled workspace inspect --json
|
|
240
|
+
|
|
241
|
+
# Then build workflows in buildOrder sequence
|
|
242
|
+
agentled wf create --pipeline '{"name":"Source — Channel A","goal":"..."}' --skip-validate
|
|
243
|
+
# ... incremental step authoring ...
|
|
244
|
+
agentled wf validate <wfId>
|
|
245
|
+
agentled wf publish <wfId> --status live
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Design rules for event-driven groups
|
|
249
|
+
|
|
250
|
+
1. **Status is the API between workflows.** One workflow writes a status; another reads it. Use `kg.upsert-rows` with `mergeStrategy: "merge"` so each workflow adds fields without wiping others.
|
|
251
|
+
|
|
252
|
+
2. **Each workflow owns one stage.** Source workflows only source. Enrich workflows only enrich. Outreach workflows only outreach. Crossing stage boundaries in one workflow makes individual retries and auditing harder.
|
|
253
|
+
|
|
254
|
+
3. **Build in build order.** A workflow that calls another via `call-workflow` must be built after the called workflow exists. The manifest `buildOrder[]` enforces this.
|
|
255
|
+
|
|
256
|
+
4. **Existing workflows are first-class.** Document reused workflows in `existingWorkflows[]` so the manifest is the complete system description, not just the new parts.
|
|
257
|
+
|
|
258
|
+
5. **Ask three goal questions before writing any step.** What is the primary goal? Where is the bottleneck? What is the system of record for the conversion event? The answers determine scope and trigger type.
|
|
259
|
+
|
|
260
|
+
## Folder layout for a workflow group
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
workflow-groups/
|
|
264
|
+
<group-slug>/
|
|
265
|
+
group.manifest.json # source of truth for the group design
|
|
266
|
+
design.md # build brief: goal, assumptions, risks, credit estimates
|
|
267
|
+
decisions/ # one file per non-trivial design decision
|
|
268
|
+
worklog.md # append-only chronological log
|
|
269
|
+
workflows/ # local pipeline JSON drafts before push
|
|
270
|
+
<workflow-id>.json
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Smoke-test plan
|
|
274
|
+
|
|
275
|
+
After building each workflow in order, smoke-test before proceeding to the next:
|
|
276
|
+
|
|
277
|
+
1. `source-*` — run once, confirm rows appear in KG with `status: "new"`
|
|
278
|
+
2. `enrich-and-score` — run on a single row, confirm enrichment fields and `status: "scored"`
|
|
279
|
+
3. `send-*-outreach` — trigger with one entity, approve draft, confirm email sends
|
|
280
|
+
4. `process-bookings` — send a test webhook, confirm status update and edition row created
|
|
281
|
+
5. `activate-*` — trigger with a forming edition, approve draft, confirm email
|
|
282
|
+
6. `edition-report` — run on a completed test edition, confirm report structure
|
|
283
|
+
|
|
284
|
+
## When not to use this pattern
|
|
285
|
+
|
|
286
|
+
- One-shot, non-recurring operation with a fixed input → single workflow.
|
|
287
|
+
- Child workflow called only via `call-workflow`, never directly → use Pattern 5 (child-workflow-contracts) instead.
|
|
288
|
+
- Real-time event response required (sub-minute) → use an `app_event` trigger on a single workflow, not a KG polling loop.
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"configuration": {
|
|
34
34
|
"contextKey": "thresholds",
|
|
35
35
|
"fields": [
|
|
36
|
-
{ "name": "metric_a_min", "label": "Metric A floor", "type": "
|
|
37
|
-
{ "name": "metric_b_max", "label": "Metric B ceiling", "type": "
|
|
38
|
-
{ "name": "metric_c_min", "label": "Metric C minimum", "type": "
|
|
36
|
+
{ "name": "metric_a_min", "label": "Metric A floor", "type": "numeric" },
|
|
37
|
+
{ "name": "metric_b_max", "label": "Metric B ceiling", "type": "numeric" },
|
|
38
|
+
{ "name": "metric_c_min", "label": "Metric C minimum", "type": "numeric" }
|
|
39
39
|
]
|
|
40
40
|
}
|
|
41
41
|
}
|