@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.
Files changed (44) hide show
  1. package/dist/commands/auth.js +18 -0
  2. package/dist/commands/auth.js.map +1 -1
  3. package/dist/commands/dryrun.d.ts +23 -0
  4. package/dist/commands/dryrun.js +641 -0
  5. package/dist/commands/dryrun.js.map +1 -0
  6. package/dist/commands/fixture.d.ts +10 -0
  7. package/dist/commands/fixture.js +155 -0
  8. package/dist/commands/fixture.js.map +1 -0
  9. package/dist/commands/group-manifest.d.ts +15 -0
  10. package/dist/commands/group-manifest.js +488 -0
  11. package/dist/commands/group-manifest.js.map +1 -0
  12. package/dist/commands/init.d.ts +17 -0
  13. package/dist/commands/init.js +83 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/onboarding.js +70 -3
  16. package/dist/commands/onboarding.js.map +1 -1
  17. package/dist/commands/test.d.ts +15 -0
  18. package/dist/commands/test.js +98 -0
  19. package/dist/commands/test.js.map +1 -0
  20. package/dist/commands/workflows.js +189 -4
  21. package/dist/commands/workflows.js.map +1 -1
  22. package/dist/commands/workspace.js +65 -0
  23. package/dist/commands/workspace.js.map +1 -1
  24. package/dist/context-schema.js +3 -4
  25. package/dist/context-schema.js.map +1 -1
  26. package/dist/index.js +10 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/utils/browser-auth.js +6 -2
  29. package/dist/utils/browser-auth.js.map +1 -1
  30. package/dist/utils/pipeline-lint.d.ts +20 -0
  31. package/dist/utils/pipeline-lint.js +348 -0
  32. package/dist/utils/pipeline-lint.js.map +1 -0
  33. package/dist/utils/preflight.js +1 -1
  34. package/dist/utils/test-runner.d.ts +85 -0
  35. package/dist/utils/test-runner.js +283 -0
  36. package/dist/utils/test-runner.js.map +1 -0
  37. package/dist/utils/workspace-folder.d.ts +46 -0
  38. package/dist/utils/workspace-folder.js +340 -0
  39. package/dist/utils/workspace-folder.js.map +1 -0
  40. package/package.json +1 -1
  41. package/patterns/v1/12-event-driven-workflow-groups.md +288 -0
  42. package/scaffolds/extract-threshold-alert.json +3 -3
  43. package/skills/agentled/SKILL.md +169 -38
  44. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentled/cli",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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": "Numeric" },
37
- { "name": "metric_b_max", "label": "Metric B ceiling", "type": "Numeric" },
38
- { "name": "metric_c_min", "label": "Metric C minimum", "type": "Numeric" }
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
  }