Recipes
Complete, runnable workflows for the patterns customers ship most often. Each recipe is a self-contained project: full skelm.config.ts, full workflow, agent definitions where used, schedule registration, and a "why each piece is here" section explaining the trade-offs.
Long-running scheduled workflows
- Coding agent on chat — webhook-triggered coding workflow with a persistent repo workspace. Receives chat messages, opens PRs, replies. The canonical "long-running agent reachable via chat" pattern.
- Ticket to PR — poll-triggered queue worker. For each new ticket, attempts the change in a per-repo persistent workspace and opens a PR. Demonstrates
forEach,compensate, and per-item workspaces. - Email triage — poll-triggered classifier. No agent loop; uses
llm()with structured output. DemonstratesforEach,branch, decision journals, and a paired digest workflow.
HTTP-triggered workflows
- HTTP enrichment — sync workflow called from existing infrastructure (queue worker, webhook). Deterministic normalization plus LLM classification, then posts to Slack. The simplest production-shaped pattern.
Observability
- OpenTelemetry traces — wire
@skelm/otelto any OTLP-compatible collector (Tempo, Jaeger, Honeycomb, Datadog). The adapter records spans; you bring the exporter.
How to use these
Every recipe has the same shape:
- Project layout — what files go where.
- Config —
skelm.config.tswith default-deny permissions, backends, secrets, server config. - Sources / agents / skills — supporting markdown and TypeScript.
- Workflow — the full TypeScript module.
- Schedule it — the CLI invocation that wires the workflow to a trigger.
- Why each piece is here — the design choices and trade-offs.
- Observability — how to inspect what happened after the fact.
Recipes are kept tight: each is a working project, not a tutorial. If you want the conceptual background, read Concepts first; if you want to ship the pattern, copy the recipe and adjust.
Choosing a recipe
| Question | Start here |
|---|---|
| "I want a chat-reachable bot that codes." | Coding agent on chat |
| "I want to autonomously work a ticket queue." | Ticket to PR |
| "I want to triage / digest a stream of items." | Email triage |
| "I want to add LLM enrichment to my existing API." | HTTP enrichment |
If your shape is none of these, mix and match. A workflow is just a typed orchestration; the recipes are not normative.
Common patterns across recipes
- Default-deny permissions. Every agent step declares exactly what it can do. There is no implicit access.
- Idempotency at the right layer. HTTP-triggered workflows use
Idempotency-Key; long-running ones usectx.state.casor watermarks; queue-driven ones use the scheduler'sdedupeKey. - Decision journals. Long-running workflows append to
ctx.state.append('decisions', ...)so a human can review what the workflow has done over time without scrolling chat history. - Structured output on
llm()andagent(). The runtime validates and the consumingcode()step does not have to guess at parsing. - Workspaces for filesystem state, KV state for structured decisions, run history for behavior, audit for security. Four artifacts; one per question.