---
title: Recipe Drafter (describe a workflow, get a validated recipe)
---

# Recipe Drafter (describe a workflow, get a validated recipe)

## What it is

> **Audience:** AI agents (with or without repo access) and Omniscio users curious how the "Create from description" flow works.

## Where to find it

This is the drafter behind the "Create from description" recipe flow, and it has no screen of its own: a caller — an agent, a script, or the recipe-creation UI — sends a description and gets a draft recipe back, along with its reasoning, warnings and any remaining validation errors.

## How it behaves

### What it does

You give Omniscio a natural-language description of an automation — "every Monday morning, check three repos for failing CI and DM me a digest" or "audit each prompt in `audit-reports/refactor-prompts/` with a 4-stage pipeline" — and Omniscio returns a draft `RecipeConfig` plus its reasoning, warnings, and any remaining validation errors. The draft is NOT saved to disk; the caller decides whether to save it (via `RECIPE_SAVE_CONFIG`), edit it, or retry.

### When the drafter refuses

- Chitchat / non-recipe requests ("what's the weather?", "1+1?")
- Requests that need capabilities Omniscio doesn't have (live web scraping, arbitrary code execution)

The output is `{ recipe: null, source: "refused", reasoning: "..." }` — caller surfaces the reasoning.

### Cost & rate behavior

- Each turn is one Sonnet Messages API call. Typical one-shot ≈ $0.05–0.15. Refinement loop ≈ $0.20–0.50.
- `maxDrafterCostUsd` halts the loop when crossed.
- The drafter does NOT cache; identical NL inputs produce a fresh call each time. (Caching is a future feature.)
- Auth: Messages API needs an API key. OAuth-only accounts get auto-fallback to the first API key account. No API key in the vault → handler returns an error envelope.

## For agents

### How it works

1. Drafter reads the bundled pattern library (`audit-pipeline`, `fan-out-consolidate`, `red-team-gate`, `validate-then-act`).
2. One Sonnet call. The system prompt has all four patterns inlined plus a brief recipe-schema cheat sheet.
3. The model returns one JSON object on the last line of its response: `{ source, patternId?, parameters?, recipe?, reasoning }`.
4. Omniscio takes the JSON:
   - `source: "pattern"` → call `instantiatePattern(patternId, parameters)` to materialize the recipe.
   - `source: "synthesized"` → use the inline `recipe` directly.
   - `source: "refused"` → drafter declined (e.g. "what's the weather?"). Returns `recipe: null`.
5. The validators (`runSemanticChecks` + `runWarningChecks`) check the candidate.
6. If blocking errors AND remaining budget, the drafter sends a refinement message containing the previous draft + the errors and asks for a fix. Up to 3 refinement turns.
7. Omniscio returns the latest draft + cost + token totals.

### IPC surface

Channel: `recipe:create-from-nl`

```jsonc
// Input
{
  "description": "audit each prompt in audit-reports/refactor-prompts/ with a 4-stage pipeline",
  "existingDraft": null,                     // optional — pass a recipe to enter edit mode
  "hints": {                                  // all optional
    "targetProjectId": "<uuid>",
    "expectedStepCount": 5,
    "preferredPatternId": "audit-pipeline",
    "suggestedBudget": { "costUsd": 25, "minutes": 240 }
  },
  "maxDrafterCostUsd": 1.0                    // optional — default $1.00
}

// Output (success envelope)
{
  "recipe": { /* RecipeConfig | null */ },
  "source": "pattern" | "synthesized" | "refused",
  "patternId": "audit-pipeline",
  "reasoning": "Matched audit-pipeline because the user described a 4-stage lane fan-out...",
  "warnings": [],                             // ValidationError[] — non-blocking
  "validationErrors": [],                     // ValidationError[] — blocking; if non-empty, recipe is best-effort
  "drafterCostUsd": 0.08,
  "drafterTokensUsed": 4200
}
```

### Iteration mode

Pass the previous output's `recipe` as `existingDraft` and a change description:

```jsonc
{
  "description": "change maxConcurrent to 3 and add a $20 cost cap",
  "existingDraft": <previous recipe>
}
```

The drafter receives an edit-mode prompt: "Apply the user request to the existing draft. Keep unchanged fields intact."

### Failure modes

- **`DRAFTER_OUTPUT_PARSE_FAILED`** — model returned text without a recoverable JSON object on the final line.
- **`PATTERN_NOT_FOUND`** — model picked a `patternId` that doesn't exist. Usually a hallucination on small models.
- **`SYNTHESIZED_RECIPE_MISSING`** — model said `source: "synthesized"` but didn't include a `recipe` field.
- **`PATTERN_INSTANTIATION_FAILED`** — model passed bad parameters (wrong type, missing required, out-of-range).
- **`STEP_CAP_EXCEEDED` / `SUBRECIPE_NOT_FOUND` / `PROJECT_NOT_FOUND` / etc.** — semantic validation errors. The drafter retries automatically; if the cap is hit before all errors clear, they surface in `validationErrors`.

### Prompts and tools

System prompts live in Main only and are NEVER returned over IPC. The `reasoning` field is the model's explanation, not the prompt.

Tools given to the model: NONE — v1 is a single-shot JSON-out call. Tool-use (Anthropic tool calls for `list_patterns`, `validate_recipe`, etc.) is a v2 consideration.

## Related

- Pattern library: [use-recipes.md](use-recipes.md), [docs/developing/RECIPE_AUTHORING_GUIDE.md](../developing/RECIPE_AUTHORING_GUIDE.md)
- Roadmap: [2026-04-22-recipe-robustness-roadmap.md](../plans/2026-04-22-recipe-robustness-roadmap.md) Phase 3 item #12
- Design doc: [2026-04-22-recipe-nl-create-ipc-design.md](../plans/2026-04-22-recipe-nl-create-ipc-design.md)
