@agentled/cli
Version:
CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.
310 lines (271 loc) • 13.9 kB
Markdown
# Pattern 12 — Event-Driven Workflow Groups
Multi-workflow systems connected through shared KG lists and status transitions.
## When to use
Use this pattern whenever the user's business process naturally decomposes into **more than one workflow** connected by shared state. Common signals:
- "Find leads, then process them on a schedule"
- "Source from multiple places into one canonical list"
- "Trigger downstream outreach when an upstream entity reaches a milestone"
- "Process inbound items, triage them, then approve a response"
- "Run sourcing weekly but scoring only when new items arrive"
A single monolithic workflow is only appropriate for truly one-shot, linear processes. Most real operations are a group.
## Core insight: workflows communicate through KG status, not direct calls
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.
```
[Workflow A] trigger → source entities → kg.upsert-rows(status: "new") → done
[Workflow B] trigger → kg.read-list(filter: "new") → enrich/score → kg.update-rows(status: "scored") → done
[Workflow C] trigger → kg.read-list(filter: "scored") → outreach → kg.update-rows(status: "outreached") → done
```
Each workflow is independently runnable, retryable, and testable. No direct dependency between them at runtime — only the KG list is shared.
## The Group Manifest
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.
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.
## Client-readable KG guide
Alongside the group manifest, create a KG text document that explains the workflow group in plain language for the client and future operators. The manifest is for builders; the KG guide is for the workspace.
Use a stable key such as `<workspace-or-pipeline>.design`, `groups.<slug>.guide`, or `pipelines.<slug>.design`.
Recommended structure:
1. What this workspace or pipeline does.
2. How the process works end to end.
3. Where records are sourced from.
4. How records are reviewed, scored, enriched, or qualified.
5. How outreach, approvals, or customer-visible actions work.
6. How recurring entities are synced, such as events, campaigns, editions, or target accounts.
7. What the current report or operating state shows.
8. Which KG lists and fields are the source of truth.
9. Which workflows belong to the group and what each one owns.
10. Operator notes and known follow-up improvements.
Put the client-facing explanation first. Put workflow IDs, implementation caveats, and debug details in an "Operator Notes" section so the document is safe to share as a first-read overview.
```json
{
"$schema": "https://schemas.agentled.app/group-manifest/v0.json",
"manifestVersion": "0.1.0",
"group": {
"id": "founder-showcase",
"name": "Founder Showcase Events",
"description": "Continuous founder sourcing → self-serve paid booking → per-edition investor activation.",
"goal": "Optimize for funnel conversion, not theme curation."
},
"operatingPrinciples": [
"Do not pick themes upfront — the first paid founder sets the edition theme and date.",
"Outreach is broad and continuous, not one batch per edition.",
"Investor outreach fires per edition, triggered by the first confirmed paid founder.",
"All customer-facing messages are approval-gated."
],
"knowledgeLists": {
"founders": {
"description": "Founders sourced and tracked through the showcase funnel.",
"userKeyField": "linkedin_url",
"fields": [
{ "name": "name", "type": "string" },
{ "name": "linkedin_url", "type": "string" },
{ "name": "domain", "type": "string" },
{ "name": "email", "type": "string" },
{ "name": "score", "type": "number" },
{ "name": "edition_id", "type": "string" },
{ "name": "status", "type": "string" }
],
"producers": ["source-channel-a", "source-channel-b"],
"consumers": ["enrich-and-score", "send-founder-outreach"]
},
"editions": {
"description": "Showcase editions, each triggered by the first paid founder confirmation.",
"userKeyField": "edition_id",
"fields": [
{ "name": "edition_id", "type": "string" },
{ "name": "theme", "type": "string" },
{ "name": "date", "type": "string" },
{ "name": "confirmed_founders", "type": "number" },
{ "name": "status", "type": "string" }
],
"producers": ["process-bookings"],
"consumers": ["activate-investors", "edition-report"]
}
},
"stateMachines": {
"founders": {
"states": ["new", "enriched", "scored", "outreached", "booked", "confirmed", "rejected"],
"initialState": "new",
"transitions": [
{ "from": "new", "to": "enriched", "trigger": "enrich-and-score" },
{ "from": "enriched", "to": "scored", "trigger": "enrich-and-score" },
{ "from": "scored", "to": "outreached", "trigger": "send-founder-outreach" },
{ "from": "outreached", "to": "booked", "trigger": "process-bookings", "description": "Stripe checkout initiated" },
{ "from": "booked", "to": "confirmed", "trigger": "process-bookings", "description": "Payment confirmed" },
{ "from": "scored", "to": "rejected", "trigger": "enrich-and-score", "description": "Score below threshold" }
]
},
"editions": {
"states": ["forming", "active", "completed"],
"initialState": "forming",
"transitions": [
{ "from": "forming", "to": "active", "trigger": "process-bookings", "description": "First paid founder confirmed" },
{ "from": "active", "to": "completed", "trigger": "edition-report" }
]
}
},
"workflows": [
{
"id": "source-channel-a",
"name": "Source — Channel A",
"purpose": "Find recently active founders from channel A and add to KG with status: new.",
"trigger": "schedule",
"schedule": "weekly",
"writes": ["founders"],
"creditEstimate": "5-10",
"status": "planned"
},
{
"id": "source-channel-b",
"name": "Source — Channel B",
"purpose": "Find founders from channel B and add to KG with status: new.",
"trigger": "schedule",
"schedule": "weekly",
"writes": ["founders"],
"creditEstimate": "5-10",
"status": "planned"
},
{
"id": "enrich-and-score",
"name": "Enrich & Score",
"purpose": "Enrich new founders via LinkedIn, find contact emails, score against ICP. Update status to scored or rejected.",
"trigger": "schedule",
"schedule": "daily",
"reads": ["founders"],
"writes": ["founders"],
"creditEstimate": "15-40",
"status": "planned"
},
{
"id": "send-founder-outreach",
"name": "Send Founder Outreach",
"purpose": "Draft personalized outreach to scored founders about the showcase opportunity. Send after approval.",
"trigger": "schedule",
"schedule": "weekly",
"reads": ["founders"],
"writes": ["founders"],
"approvalGates": ["draft-outreach-email"],
"creditEstimate": "5-15",
"status": "planned"
},
{
"id": "process-bookings",
"name": "Process Bookings",
"purpose": "Receive Stripe webhook or booking form submission, update founder status to booked/confirmed, and create/update edition when first founder confirms.",
"trigger": "webhook",
"reads": ["founders"],
"writes": ["founders", "editions"],
"creditEstimate": "2-5",
"status": "planned"
},
{
"id": "activate-investors",
"name": "Activate Investors",
"purpose": "When a new edition activates (first confirmed founder), reach out to investors for that edition's theme. Send after approval.",
"trigger": "schedule",
"schedule": "daily",
"reads": ["editions"],
"approvalGates": ["draft-investor-email"],
"creditEstimate": "5-15",
"status": "planned"
},
{
"id": "edition-report",
"name": "Edition Report",
"purpose": "Generate a post-event report for each completed edition: attendees, scores, conversion rate.",
"trigger": "schedule",
"reads": ["founders", "editions"],
"writes": ["editions"],
"creditEstimate": "5-10",
"status": "planned"
}
],
"existingWorkflows": [
{
"workflowId": "<existing-source-workflow-id>",
"name": "Existing source workflow",
"action": "reuse",
"notes": "Already sources entities. Will be wired to write to the founders KG list with status: new."
}
],
"integrations": {
"agentled": { "required": true, "purpose": "LinkedIn enrichment" },
"hunter": { "required": true, "purpose": "Email finding" },
"kg": { "required": true, "purpose": "KG list reads/writes for founders and editions" },
"webhook": { "required": true, "purpose": "Stripe booking webhooks" },
"gmail": { "required": true, "purpose": "Outreach email sending" }
},
"workspaceSettings": [
"Outreach profile configured with sender name and connected Gmail account",
"Stripe webhook URL registered and pointing to process-bookings trigger"
],
"buildOrder": [
"source-channel-a",
"source-channel-b",
"enrich-and-score",
"send-founder-outreach",
"process-bookings",
"activate-investors",
"edition-report"
],
"openQuestions": [
{
"question": "What is the primary success metric: paid bookings per month, qualified entities contacted, or investor confirmations per edition?",
"impact": "Determines which KPI to surface in the edition report and which step outputs to track in analytics.",
"status": "open"
},
{
"question": "What is the system of record for bookings: Stripe webhook, a form provider, or a manual update?",
"impact": "Determines trigger type and input schema of process-bookings workflow.",
"status": "open"
},
{
"question": "How many entities constitute a full edition, and what is the minimum to activate downstream outreach?",
"impact": "Entry condition logic in activate-investors and edition creation in process-bookings.",
"status": "open"
}
]
}
```
## CLI commands
```bash
# Scaffold a starter manifest for your pattern
agentled group-manifest scaffold event-driven-funnel --out workflow-groups/<group-slug>/group.manifest.json
# Edit the manifest to match your actual workspace
# Validate locally before touching the API
agentled group-manifest validate workflow-groups/<group-slug>/group.manifest.json
# One-shot workspace orientation (run at session start)
agentled workspace inspect --json
# Then build workflows in buildOrder sequence
agentled wf create --pipeline '{"name":"Source — Channel A","goal":"..."}' --skip-validate
# ... incremental step authoring ...
agentled wf validate <wfId>
agentled wf publish <wfId> --status live
```
## Design rules for event-driven groups
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.
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.
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.
4. **Existing workflows are first-class.** Document reused workflows in `existingWorkflows[]` so the manifest is the complete system description, not just the new parts.
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.
## Folder layout for a workflow group
```
workflow-groups/
<group-slug>/
group.manifest.json # source of truth for the group design
design.md # build brief: goal, assumptions, risks, credit estimates
decisions/ # one file per non-trivial design decision
worklog.md # append-only chronological log
workflows/ # local pipeline JSON drafts before push
<workflow-id>.json
```
## Smoke-test plan
After building each workflow in order, smoke-test before proceeding to the next:
1. `source-*` — run once, confirm rows appear in KG with `status: "new"`
2. `enrich-and-score` — run on a single row, confirm enrichment fields and `status: "scored"`
3. `send-*-outreach` — trigger with one entity, approve draft, confirm email sends
4. `process-bookings` — send a test webhook, confirm status update and edition row created
5. `activate-*` — trigger with a forming edition, approve draft, confirm email
6. `edition-report` — run on a completed test edition, confirm report structure
## When not to use this pattern
- One-shot, non-recurring operation with a fixed input → single workflow.
- Child workflow called only via `call-workflow`, never directly → use Pattern 5 (child-workflow-contracts) instead.
- Real-time event response required (sub-minute) → use an `app_event` trigger on a single workflow, not a KG polling loop.