plungeai-workflows
Workflows — CNL orchestration: what it is and how to run it
A workflow is a YAML document (CNL — Cognitive Natural Language) that the engine executes as a DAG of agent calls: parallel fan-out, sequential pipelines, unlimited nesting, conditions, batches, debates, and bounded autonomous missions.
A workflow is a YAML document (CNL — Cognitive Natural Language) that the engine executes as a DAG of agent calls: parallel fan-out, sequential pipelines, unlimited nesting, conditions, batches, debates, and bounded autonomous missions. The engine is pure orchestration — it routes tasks to agents over RPC and coordinates data handoff; agents do the actual work.
Authoring belongs to this
plungeai-workflowsskill — full CNL spec, validation rules, recipes, and runnable examples (references/cnl-spec.md,recipes.md,examples/). This reference is the platform-level view: what workflows can do, the facts that shape good designs, and every way to execute one. Do not write non-trivial YAML without the authoring skill loaded.
Shape of a workflow
name: Research and synthesize
tasks:
- type: parallel
id: research
subtasks:
- { type: task, id: web, agent: brave-agent, query: "{input}" }
- { type: task, id: deep, agent: exa-agent, query: "{input}" }
- type: task
id: synthesize
agent: llm-agent
prompt: "Synthesize the research above into a brief on: {input}"Data flows automatically: each task's result lands in SharedMemory and downstream tasks receive upstream content — you never hand-wire results. Ten task types exist:
| Type | One line |
|---|---|
task | Single agent call (the default) |
parallel | All subtasks dispatched at once |
sequential | subtasks in order, each seeing prior results |
dynamic | Generator agent produces N items → executors fan out over them |
batch | Per-item pipeline over a list — isolated; per-item tracking + resume is opt-in track: true |
debate | N debaters, up to 5 rounds, optional judge |
validate | Macro → parallel PASS/FAIL validators + judge verdict |
output | Deliver a previous result (email/document) with a format transform |
harness | Bounded autonomous mission — see plungeai-missions |
agent_loop | Legacy alias for harness — prefer harness |
Blocks (parallel/sequential/batch/dynamic/debate/validate) never carry
agent: themselves; agents go on inner tasks. Conditions (condition: CEL, e.g.
input.contains('urgent') or qa_gate.verdict == "FAIL") gate any task or block
before dispatch.
Parallelism — the facts that should shape your designs
These are structural (read from engine code) and measured (from the engine's own server-side event timestamps):
type: paralleldispatches every subtask at once — onePromise.all, no chunking, no width cap. Each child is a separate Worker invocation over RPC.- Engine overhead is zero at every width measured (1 → 100 children): dispatch spread 0 ms; workflow total equals the slowest child to the millisecond. A parallel block costs the slowest of N, not N of anything. The same 12 search tasks: sequential 18.9 s, parallel 1.9 s.
- I/O-bound fan-out is flat — children waiting on networks do not contend, even against the same agent service.
- CPU-bound or isolate-heavy children queue at the destination, not in the engine. Practical width against ONE shared destination: ~12-50 depending on the service; past that you are queueing, not gaining. To go wider, spread children across distinct destinations (a real research fan-out across six search providers does this naturally).
- Every child's result is durable in SharedMemory — collect at the end or pick up each as it lands.
- Measure with an A/B (
sequentialvsparallelof the same tasks), never by dividing a parallel total by a single-task average.
Inputs and placeholders
{input} is the primary input; {input1}…{input10} and any custom
{semantic_name} also work — declared under inputs: and replaced in one pass.
Date tokens (for rolling windows — scheduled runs especially): {now}, {today},
{yesterday}, {week_start}, {week_end}, {last_week_start}, {last_week_end},
{month_start}, {month_end}, {last_month_start}, {last_month_end}. All UTC,
ISO weeks (Monday first), seeded from the run's execution time; a caller-supplied
input of the same name wins. See plungeai-scheduling for the pattern.
Search-query guard: search agents cap query length (per their cards) and an
over-limit query — typed or auto-filled from a prior task's output — returns a
structured needs_input refusal; nothing is silently truncated. Give search tasks
short explicit queries; bulk upstream data flows as context, never as the query.
Executing a workflow
MCP (preferred when operating live)
# Ad-hoc (test before saving — always)
plungeai_execute_workflow {workflow_yaml: "<yaml>", input: "solid-state batteries"}
# Long run
plungeai_execute_workflow {workflow_yaml: "<yaml>", input: "…", mode: "async"}
# → execution_id → plungeai_get_workflow_status → plungeai_get_result
# Saved workflow by id
plungeai_execute_workflow {workflow_id: "…", input: "…"}YAML gotchas the platform will refuse: hard-wrapped values (use block scalars
prompt: |), unquoted values containing colons, invented fields (depends_on,
outputs:, parallel: true, schedule: — scheduling is a separate system, see
plungeai-scheduling). Or skip hand-writing: plungeai_build_workflow {goal}
generates and saves one via the platform builder.
Manage saved workflows with plungeai_workflow
(action: create|get|update|delete|save_version|list_versions|get_version|restore_version)
— create/update/delete sync live to Studio and peer apps. Create/update also take
folder (file/move it — "none" clears) and kind: workflow|agent|bot (which
sidebar section it lives in: Flows, Agents, or Bot agents). This is the mechanic
behind the "my agents" terminology: a saved item with kind: agent IS the user's
"agent", and plungeai_list_workflows filters by folder. Server-side validation
on save is your final gate: a refusal lists field errors; fix exactly those.
Pre-built starting points: plungeai_templates {action: list|get|use}.
One API
# Inline (JSON wrapper or raw text/yaml body)
curl -s -X POST https://api.plungeai.com/v1/workflows/execute \
-H "Authorization: Bearer ozk_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"workflow": "name: quick\ntasks:\n - agent: brave-agent\n query: \"{input}\"\n", "input": "latest AI news"}'
# → {success, workflow_id, final_task_id, request_id}
# Saved workflow by id (resolved against your user)
curl -s -X POST https://api.plungeai.com/v1/workflows/{id}/execute \
-H "Authorization: Bearer ozk_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"input": "latest AI news"}'
# Live progress: SSE stream of engine events
curl -N -X POST https://api.plungeai.com/v1/workflows/{id}/execute-stream \
-H "Authorization: Bearer ozk_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"input": "…"}'The acknowledgement is a pointer, not the content. Redeem results:
curl -s https://api.plungeai.com/v1/workflows/results/{workflowId}/{taskId} \
-H "Authorization: Bearer ozk_YOUR_KEY"
# 200 {content, content_type, workflow_id, task_id} | 404 not_ready (still running)final_task_id from the acknowledgement is the task id of the final result; any
intermediate task id works too (read one branch of a fan-out). Errors:
400 missing_workflow, 404 workflow_not_found, 502 engine_error.
Studio
Humans edit and run workflows visually; the Code tab accepts pasted YAML with inline validation. Point users there for visual work and credential connects.
Reading results
- MCP:
plungeai_get_result {workflow_id}→ final output (full conversation thread for conversational runs); addtask_idfor a single step. Output is final user-ready markdown — relay verbatim. - One API: the
/results/endpoints above. - Execution completed but "results not yet available" → retry
plungeai_get_result; storage is eventually consistent by a beat.
Follow-ups
A completed execution can be continued conversationally:
plungeai_followup {execution_id, prompt: "<follow-up question>"} — prompt is the
required parameter (message belongs to plungeai_continue, the HITL resume tool —
do not cross them). It reuses the run's context. Workflows
may carry a followup: block (provider/model/memory_scope/temperature/max_tokens)
which the engine ignores at execution time; it configures this follow-up behavior.
Full conversation/HITL detail: plungeai-results-traces.
Failure semantics worth knowing
- Parallel branches are best-effort: a failed branch does not abort the run; the
summary carries
failed_branches[](present only when something was lost). Check it before trusting a synthesis over fan-out results. - Per-task
retry: N(max 2) exists for plaintype: taskonly — never forharness— and re-fires the FULL agent call: set it only on read-only/idempotent agents, never payment/messaging/automation classes. - The execution summary (
total_tasks_executed,tasks_skipped,parallel_blocks_executed, per-task durations) is your first debugging read — seeplungeai-results-traces.