/dynamic-workflows
How to write JavaScript workflow scripts for the `workflow` tool - fanning work out across many isolated subagents with agent(), parallel(), and pipeline(), then synthesizing one result. Use when a task decomposes into several independent investigations or changes (codebase
$ npx -y skills add posthog/posthog --skill dynamic-workflows --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/dynamic-workflows
Context preview
The summary Claude sees to decide when to auto-load this skill.
How to write JavaScript workflow scripts for the `workflow` tool - fanning work out across many isolated subagents with agent(), parallel(), and pipeline(), then synthesizing one result. Use when a task decomposes into several independent investigations or changes (codebase
SKILL.md
dynamic-workflows.SKILL.mdname: dynamic-workflows
description: How to write JavaScript workflow scripts for the `workflow` tool - fanning work out across many isolated subagents with agent(), parallel(), and pipeline(), then synthesizing one result. Use when a task decomposes into several independent investigations or changes (codebase audits, many-file analysis, wide research, multi-perspective review, applying the same edit across many independent files).
Dynamic Workflows
The `workflow` tool executes a JavaScript orchestration script you write. The script holds the loop, branching, and intermediate results; each `agent()` call runs one isolated subagent in its own pi process; only the script's return value comes back into your context. This is how you audit 20 files, research 8 topics, or apply the same change across 20 independent files without burning your own context window on the intermediate output.
When to use it
- The work decomposes into **several independent investigations or changes** whose
intermediate outputs you don't need verbatim - only a synthesis (or a report of what changed).
- Examples: audit every route/module for a property, summarize each package of a
monorepo, verify a list of findings adversarially, research N alternatives, rename an API across every file that references it.
Do **not** use it for: a single question or a single edit (use `subagent` or just do it directly), one or two parallel tasks (use `subagent` parallel mode), or work needing your full conversation context.
Script shape
Prefer the **strict declared-plan contract** below. Strict mode turns on only when `meta.phases` is a literal object the runtime can read without executing code; older/dynamic scripts keep their legacy behavior. Do not set token budgets: choose only the appropriate persona/model tier and let the host account actual usage.
export const meta = {
name: 'audit_routes',
goal: 'Produce a decision-ready router audit',
inputs: ['repository'],
phases: [
{ title: 'Scan', goal: 'Map routers', inputs: ['repository'], produces: ['router inventory'] },
{ title: 'Audit', goal: 'Check the inventory', inputs: ['router inventory'], produces: ['router audits'] },
{ title: 'Synthesize', goal: 'Deliver the verdict', inputs: ['router audits'], produces: ['audit verdict'] },
],
synthesis: { phase: 'Synthesize', inputs: ['router audits'], produces: ['audit verdict'] },
}
phase('Scan')
const inventory = await agent(
'List every *.router.ts file under packages/host-router/src/routers. Reply with only JSON.',
{
label: 'route inventory',
objective: 'Produce the complete router inventory for the audit.',
inputs: ['repository'],
produces: 'router inventory',
schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },
},
)
if (!inventory) return { ok: false, error: 'inventory failed' }
phase('Audit')
const audits = await agent(
'Audit the router inventory against the one-line-forward rule. Return every violation as JSON.',
{ label: 'router audit', objective: 'Audit all discovered routers for inline logic.', inputs: ['router inventory'], produces: 'router audits', schema: { type: 'object', required: ['violations'] } },
)
phase('Synthesize')
const verdict = await agent(
'Summarize the supplied router audits into {ok, violations: [...]}. Reply with only JSON.',
{ label: 'final verdict', agent: 'Plan', objective: 'Create the final decision-ready audit report.', inputs: ['router audits'], produces: 'audit verdict', schema: { type: 'object', required: ['ok', 'violations'] } },
)
return verdictIn strict mode, activate declared phases exactly in order; agent inputs are artifact-name arrays (not inline records), every declared phase output must be published exactly once (an agent automatically publishes its declared `produces`, or use `publish(name, value)` for aggregates), and the final `synthesis` phase must publish its named final artifact. Give every phase a goal, every agent a unique label and objective, and all real handoffs named inputs/outputs.
Rules: plain JavaScript (no TypeScript, no `import`/`require`); the leading `export const meta = { name, description }` is optional but conventional; the script must call `agent()` at least once; the return value must be JSON-serializable (a common mistake is returning an unawaited `agent()` promise).
API
| Global | Behavior | |--------|----------| | `agent(prompt, opts)` | Runs one subagent; resolves to its final text, or the parsed+shape-checked object when `opts.schema` is set, or `null` on failure. Opts: `label` (short, unique - drives the live display), `objective` (responsibility), `inputs` (artifact-name strings or a record of named string values), `produces` (one artifact name), `agent` (`'Explore'` default, `'Plan'`, or `'General'`), `schema` (plain JSON Schema), `cwd`, `model` (tier keyword, see below). | | `parallel(thunks)` | `await parallel(items.map(i => () => agent(...)))` - functions, **not** promises. Results in input order; failed branches are `null`. | | `pipeline(items, ...stages)` | Fans items through sequential stages (map → verify → summarize). Items run concurrently; each item's stages run in order; each stage receives `(previousValue, originalItem, index)`. A failed stage nulls that item's slot. | | `phase(title, meta?)` | Marks a new stage of work for the live progress display. Prefer `phase('Audit', { goal: '...', inputs: ['inventory'], produces: ['findings'] })` so the upcoming plan and dependencies are visible before it runs. `goal`, `inputs`, and `produces` are optional; dynamic/conditional phases remain supported. | | `log(message)` | Appends a workflow-level log line (shown in the expanded view). | | `parseJson(text)` | Extracts JSON from an agent's text reply, tolerating fences and surrounding prose. Prefer `schema` on `agent()` instead. | | `args` | The JSON value passed in the tool ca
Read more
name: dynamic-workflows description: How to write JavaScript workflow scripts for the `workflow` tool - fanning work out across many isolated subagents with agent(), parallel(), and pipeline(), then synthesizing one result. Use when a task decomposes into several independent investigations or changes (codebase audits, many-file analysis, wide research, multi-perspective review, applying the same edit across many independent files).
Dynamic Workflows
The `workflow` tool executes a JavaScript orchestration script you write. The script holds the loop, branching, and intermediate results; each `agent()` call runs one isolated subagent in its own pi process; only the script's return value comes back into your context. This is how you audit 20 files, research 8 topics, or apply the same change across 20 independent files without burning your own context window on the intermediate output.
When to use it
- The work decomposes into **several independent investigations or changes** whose
intermediate outputs you don't need verbatim - only a synthesis (or a report of what changed).
- Examples: audit every route/module for a property, summarize each package of a
monorepo, verify a list of findings adversarially, research N alternatives, rename an API across every file that references it.
Do **not** use it for: a single question or a single edit (use `subagent` or just do it directly), one or two parallel tasks (use `subagent` parallel mode), or work needing your full conversation context.
Script shape
Prefer the **strict declared-plan contract** below. Strict mode turns on only when `meta.phases` is a literal object the runtime can read without executing code; older/dynamic scripts keep their legacy behavior. Do not set token budgets: choose only the appropriate persona/model tier and let the host account actual usage.
export const meta = {
name: 'audit_routes',
goal: 'Produce a decision-ready router audit',
inputs: ['repository'],
phases: [
{ title: 'Scan', goal: 'Map routers', inputs: ['repository'], produces: ['router inventory'] },
{ title: 'Audit', goal: 'Check the inventory', inputs: ['router inventory'], produces: ['router audits'] },
{ title: 'Synthesize', goal: 'Deliver the verdict', inputs: ['router audits'], produces: ['audit verdict'] },
],
synthesis: { phase: 'Synthesize', inputs: ['router audits'], produces: ['audit verdict'] },
}
phase('Scan')
const inventory = await agent(
'List every *.router.ts file under packages/host-router/src/routers. Reply with only JSON.',
{
label: 'route inventory',
objective: 'Produce the complete router inventory for the audit.',
inputs: ['repository'],
produces: 'router inventory',
schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },
},
)
if (!inventory) return { ok: false, error: 'inventory failed' }
phase('Audit')
const audits = await agent(
'Audit the router inventory against the one-line-forward rule. Return every violation as JSON.',
{ label: 'router audit', objective: 'Audit all discovered routers for inline logic.', inputs: ['router inventory'], produces: 'router audits', schema: { type: 'object', required: ['violations'] } },
)
phase('Synthesize')
const verdict = await agent(
'Summarize the supplied router audits into {ok, violations: [...]}. Reply with only JSON.',
{ label: 'final verdict', agent: 'Plan', objective: 'Create the final decision-ready audit report.', inputs: ['router audits'], produces: 'audit verdict', schema: { type: 'object', required: ['ok', 'violations'] } },
)
return verdictIn strict mode, activate declared phases exactly in order; agent inputs are artifact-name arrays (not inline records), every declared phase output must be published exactly once (an agent automatically publishes its declared `produces`, or use `publish(name, value)` for aggregates), and the final `synthesis` phase must publish its named final artifact. Give every phase a goal, every agent a unique label and objective, and all real handoffs named inputs/outputs.
Rules: plain JavaScript (no TypeScript, no `import`/`require`); the leading `export const meta = { name, description }` is optional but conventional; the script must call `agent()` at least once; the return value must be JSON-serializable (a common mistake is returning an unawaited `agent()` promise).
API
| Global | Behavior | |--------|----------| | `agent(prompt, opts)` | Runs one subagent; resolves to its final text, or the parsed+shape-checked object when `opts.schema` is set, or `null` on failure. Opts: `label` (short, unique - drives the live display), `objective` (responsibility), `inputs` (artifact-name strings or a record of named string values), `produces` (one artifact name), `agent` (`'Explore'` default, `'Plan'`, or `'General'`), `schema` (plain JSON Schema), `cwd`, `model` (tier keyword, see below). | | `parallel(thunks)` | `await parallel(items.map(i => () => agent(...)))` - functions, **not** promises. Results in input order; failed branches are `null`. | | `pipeline(items, ...stages)` | Fans items through sequential stages (map → verify → summarize). Items run concurrently; each item's stages run in order; each stage receives `(previousValue, originalItem, index)`. A failed stage nulls that item's slot. | | `phase(title, meta?)` | Marks a new stage of work for the live progress display. Prefer `phase('Audit', { goal: '...', inputs: ['inventory'], produces: ['findings'] })` so the upcoming plan and dependencies are visible before it runs. `goal`, `inputs`, and `produces` are optional; dynamic/conditional phases remain supported. | | `log(message)` | Appends a workflow-level log line (shown in the expanded view). | | `parseJson(text)` | Extracts JSON from an agent's text reply, tolerating fences and surrounding prose. Prefer `schema` on `agent()` instead. | | `args` | The JSON value passed in the tool ca
:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.
Repo: posthog/posthog
Other skills on posthog.
- /analyzing-expensive-users
Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.
Open skill - /creating-online-evaluations
Author continuously-running online evaluations in PostHog AI observability, grounded in real failure modes you've identified. Use when the user wants evaluations that automatically score new generations or whole traces going forward — "create an eval to catch X", "continuously
Open skill - /exploring-ai-failures
Find where an AI/LLM application is failing in production and surface the failure patterns, working from real traces. Use when someone wants to understand what's going wrong with an AI feature, find and categorize failure modes, triage errors, or investigate quality issues
Open skill - /exploring-llm-clusters
Investigate AI observability clusters — understand usage patterns in AI/LLM traffic, compare cluster behavior, compute cost/latency metrics, and drill into individual traces within clusters.
Open skill - /exploring-llm-costs
Investigate LLM spend in PostHog — total cost over time, cost by model, provider, user, trace, or custom dimension, token and cache-hit economics, and cost regressions. Use when the user asks "how much are we spending on LLMs?", "which model / user / feature is most expensive?",
Open skill - /exploring-llm-evaluations
Investigate AI observability evaluations — `hog` (deterministic code-based), `llm_judge` (LLM-prompt-based), and `sentiment` (user-message sentiment). Find existing evaluations, inspect their configuration, run them against specific generations, query individual results, and
Open skill

