The conversion guide
The instructions an AI assistant follows to turn a recipe into CKL. You do not need to read this — the contribute page copies it for you.
Turn Any Recipe Into CKL
This page is written for an AI assistant. Copy the whole thing into ChatGPT,
Claude, Gemini, or any other assistant, then give it your recipe — as typed
text, a photo of a cookbook page, a screenshot, or a PDF. It will ask you about
anything the original recipe leaves vague and hand back a finished file you can
submit at cookbook.edeny.dev/contribute/.
If you are a human reading this: you do not need to understand any of it. Skip
to the contribute page and follow the
three steps there.
Your task
You are converting a recipe into CKL (Culinary Knowledge Language), the
notation behind The Cook Book. Your output is a single .ckl.md file.
CKL exists because ordinary recipes are ambiguous. "Cook the chicken until
done", "season to taste", "a splash of oil" — these carry real information in a
cook's head and none on the page. CKL refuses to accept them. Every quantity,
time, and temperature is a number with a unit.
That means you will be missing information, and the single most important
thing you do is ask the person for it rather than inventing it. A fabricated
oven temperature in a recipe someone else will cook is worse than no recipe.
How to work
- Read the recipe. If it is an image or PDF, transcribe it first and show
- List what is missing or vague. Work through the checklist in
- Ask — all at once, numbered, in plain language. Not an interrogation
person can say "all defaults are fine" and be done.
> 3. The recipe says "a knob of butter" — roughly how much? (I'll assume
> 30 g if you're not sure.)
- Say which numbers you assumed after they answer, so nothing is silently
- Write the file, then check it against
- Output the complete file in one code block so it can be copied in a
three-backtick block of its own — and keep all commentary outside it.
What to ask about
- Vague amounts — "a pinch", "to taste", "a splash", "1 large onion",
(1 medium onion ≈ 150 g, 1 garlic clove ≈ 5 g, 1 large egg ≈ 50 g) and *say
that you did*; ask when it genuinely matters.
- Missing temperatures — "medium heat", "a hot pan", "moderate oven". Ask,
moderate oven ≈ 180 °C).
- Missing times — "until golden", "until fragrant", "until tender". Ask for
## Notes section; it is useful to
a cook even though CKL will not accept it as an instruction.
- Doneness for meat and fish — always ask how they check it. Poultry has a
- Servings — required, and often unstated.
- Equipment — pan size, pot size, whether an oven is involved.
- Prep vs. cook time — for the file's frontmatter.
Do not ask about: the intro paragraph, the tags, or the notes. Write those
yourself from what you have.
The file you produce
<!--ckl-code-0-->ckl
servings 4
ingredient "bread" 300g
ingredient "tomato" 300g
ingredient "garlic" 10g
ingredient "basil" 8g
ingredient "olive oil" 40ml
ingredient "salt" 3g
pan "grill pan" 28cm
knife "chef's knife"
cut "bread" style slice size 15mm
cut "tomato" style dice size 8mm
cut "garlic" style crush
# Toss the diced tomato with basil, olive oil, and salt
toss "tomato" "basil" with "olive oil" "salt"
# Toast the bread slices until charred on both sides
toast "bread" in pan at 220C for 4min
# Rub the warm bread with the crushed garlic
season "bread" with "garlic"
assemble "tomato" "basil" on "bread"
rest 1min
<!--ckl-code-1-->
Frontmatter rules
The frontmatter reader is a plain key: value line splitter, not YAML.
| Field | Required | Notes |
|---|---|---|
title | yes | Plain text, one line, no quotes, no brackets |
description | yes | One line, under 160 characters. It is the card subtitle |
tags | recommended | Bare comma-separated list: tags: italian, pasta. Never tags: [italian, pasta] — the brackets end up in the tag names |
prepTime | recommended | Whole minutes, digits only: prepTime: 10 |
cookTime | recommended | Whole minutes, digits only |
No other fields are read. No nesting, no YAML lists, no quoted values, and never
a line break inside a value.
The rest of the file
# Titlematching the frontmatter title.- One or two paragraphs of prose. Say what the dish is and what makes it good.
## Recipecontaining exactly one`cklblock.## Noteswith 2–5 bullets. This is where the doneness cues go — "until
along with substitutions and make-ahead advice. CKL carries the numbers; the
notes carry the judgement.
There is no syntax for a visual or tactile doneness cue and you should not
invent one. until golden_edges and until soft_center are not valid CKL and
never will be — how a bake looks depends on the pan, the oven and the sugar,
so the language states the time and temperature that control it and leaves the
appearance to prose. A baked recipe should carry a note saying what to look
for, phrased for a cook standing at the oven:
> What to look for at the end: golden rims, a centre that still looks
> slightly wet and gives when pressed. It will not look done. It is done.
The one place a qualitative endpoint is valid CKL is where it ends an action
you control continuously — cream ... until fluffy, mix ... until smooth,
reduce ... until nappe — and only using a word from the fixed lists in this
guide.
The CKL language
Inside the `ckl block, every line must begin with a keyword, or be
blank, or be a # comment. There is no prose. A single bad line fails the whole
file.
Values
| Kind | Form | Examples |
|---|---|---|
| Ingredient name | double-quoted string | "chicken breast", "olive oil" |
| Equipment / stage reference | bare identifier | pan, pot, oven, bowl, zabaione |
| Equipment name | double-quoted string | "cast iron skillet" |
| Number + unit | digits, no space before the unit | 500g, 180C, 6min, 28cm |
| Comment | # to end of line | # Rest before slicing |
The twelve units are the only ones that exist:
g, kg, ml, l, C, min, s, cloves, cm, mm, times, additions.
The last two are counts, used only by tap and add (see
There are no cups, tablespoons, ounces, pounds, Fahrenheit, hours, or "each".
Convert everything:
- 1 cup flour ≈ 120 g · 1 cup water ≈ 240 ml · 1 tbsp ≈ 15 ml · 1 tsp ≈ 5 ml
- 1 oz ≈ 28 g · 1 lb ≈ 454 g
- °F → °C: subtract 32, multiply by 5/9, then round to a sensible number
- 1 hour 30 minutes →
90min - Count items → mass: 1 medium onion ≈ 150 g, 1 carrot ≈ 70 g, 1 garlic clove
Temperatures are always Celsius. Times are always minutes unless
genuinely shorter than a minute.
Structure of a block
Declarations first, then actions, in this order:
servings 4 # exactly one, first
ingredient "bread" 300g # every ingredient, with an amount
ingredient "olive oil" 40ml
pan "grill pan" 28cm # every piece of equipment
knife "chef's knife"
cut "bread" style slice size 15mm # then the actions, in cooking order
toast "bread" in pan at 220C for 4minNothing may be referenced before it is declared.
Declarations
servings 4
ingredient "chicken breast" 600g
ingredient "olive oil" 30ml
pan "cast iron skillet" 28cm
pot "stock pot" 8l
oven "standard oven"
bowl "mixing bowl" 3l
knife "chef's knife"
sheet "baking sheet" 46cm
refrigerator "refrigerator"ingredient "<name>" <number><unit>— an amount is mandatory. There is no
- An equipment declaration is always a keyword, then a quoted name. There
pan, pot,
oven, bowl, knife, sheet, refrigerator. The size is optional.
The keyword is the kind of vessel; the quoted name is the specific item.
A cooling rack is not its own keyword — it is declared oven "wire cooling
rack", because that is the keyword its database entry allows. Writing
wire_cooling_rack "wire cooling rack" fails with E001, because the parser
reaches a word that is not a keyword and has nothing to do with it.
Copy the whole line from the list below. Do not assemble one yourself
from a name and a keyword you think fits.
- A
sheetis a baking sheet or tray — a surface you arrange things on. It is
oven.
- Declare a
refrigeratorwhenever you usechill— that is a hard error
- Declare a
knifewhenever you usecut— that is a hard error otherwise
- An unknown quoted name is a hard error (E104).
- You get one of each equipment type per recipe. Two pans means using
stage (see below).
Every equipment declaration there is
Copy one of these lines verbatim. Any name or alias on a line is accepted, so
pan "skillet" and pan "cast iron skillet" are the same declaration.
pan
pan "baking dish"— also: casserole dish, gratin dish, square dish, oven dishpan "cast iron skillet"— also: cast iron pan, skillet, frying pan, large skillet, wide skilletpan "grill pan"— also: griddle pan, indoor grill, flat griddlepan "non-stick pan"— also: teflon pan, coated panpan "stainless steel pan"— also: stainless pan, sauté panpan "wok"— also: stir-fry pan
pot
pot "dutch oven"— also: braiser, cocotte, casserole potpot "saucepan"— also: small pot, sauce pot, wide saucepanpot "stock pot"— also: large pot, pasta pot, soup pot, deep pot
oven
oven "oven"— also: main oven, standard oven, conventional oven, home ovenoven "roasting pan"— also: roasteroven "wire cooling rack"— also: cooling rack, oven rack, wire rack
bowl
bowl "mixing bowls"— also: prep bowls, bowls, mixing bowl
knife
knife "chef's knife"— also: chef knife, cook's knife, gyuto
sheet
sheet "baking sheet"— also: sheet pan, cookie sheet, half sheet pan
refrigerator
refrigerator "refrigerator"— also: fridge, chiller
Not declarable. These are in the equipment database but have no CKL
keyword, so they cannot appear in the block at all. If one matters, say so
in the notes instead:
colander · cutting board · fine-mesh strainer · immersion blender · instant-read thermometer · kitchen scale · kitchen tongs · pepper mill · spatula (silicone) · whisk · wooden spoon
Technique statements — prefer these
This is the modern, preferred form. Every technique takes the same optional
clauses, in any order:
<technique> "target"... [in <equipment>] [with "medium"...] [on "surface"] [at <temp>] [for <time>]The available techniques:
sear · saute · fry · boil · simmer · braise · roast · bake ·
poach · sweat · render · toast · blend · bloom · infuse ·
season · toss · assemble · dust
Real examples:
sear "chicken breast" in pan with "olive oil" at 180C for 6min
saute "onion" "carrot" "celery" in pot with "olive oil" at 140C for 8min
boil "spaghetti" in pot with "water" "salt" at 100C for 10min
simmer "tomato" "garlic" in pot with "olive oil" "salt" at 130C for 15min
braise "beef" in pot with "salt" "black pepper" for 90min
roast "beef steak" "onion" "carrot" in oven at 220C for 30min
bake "chicken breast" "mozzarella" in oven at 200C for 8min
render "guanciale" in pan at 130C for 8min
poach "eggs" in pot for 4min
sweat "onion" "garlic" in pot with "olive oil" at 130C for 5min
bloom "paprika" "caraway seeds" in pot for 1min
infuse "garlic" "chili flakes" in pan with "olive oil" at 120C for 3min
toast "bread" in pan at 220C for 4min
blend "chickpeas" "tahini" "lemon juice" in pot
season "potatoes" with "cream" "vinegar"
toss "tomato" "basil" with "olive oil" "salt"
assemble "tomato" "basil" on "bread"Note the pattern: what you are cooking goes in the leading quoted strings,
the fat or liquid it cooks in goes after with.
There is no until clause on a technique — doneness goes on its own line
immediately after (see Conditions).
A technique may also target a stage instead of ingredients, written as a
bare identifier. Use this when what goes in the oven is a mixture rather than a
list of ingredients — a tray of cookie dough is not "flour and butter":
bake dough in oven at 180C for 9minChanging the temperature mid-recipe needs no special syntax. State the new
temperature on the next technique:
heat oven to 180C
bake dough in oven at 180C for 9min
bake dough in oven at 170C for 4minDo not write a heat line that disagrees with the technique beside it — a
heat oven to 170C followed by bake ... at 180C is an error (E302).
Preparation statements
cut "onion" style dice size 8mm
cut "garlic" style crushstyle takes a bare identifier: dice, slice, mince, chop, julienne,
grate, crush, pound, brunoise, chiffonade. size is optional.
mix bowl until combined
mix zabaione while heating to 65C until smooth
knead bowl until elastic
shape bowl into balls size 3cm
coat "chicken breast" with "flour"
soak "chickpeas" in "water" 480min
drain potThe bare identifier after until is a state: combined, uniform, smooth,
cohesive, emulsified, elastic, tacky, homogeneous, stiff, folded.
The one after into is a form: balls, patties, logs, rounds, discs,
sheets, strips, dumplings, loaves, rolls, squares, wedges.
shape also takes an optional weight: shape bowl into balls weight 60g.
Baking and pastry
Use these for anything baked. They are not optional flavour — mix where the
recipe says fold, or rest where it says refrigerate, records the wrong
instruction.
cream "butter" "sugar" in dough until fluffy
add "eggs" to dough in 2additions while mixing
add "flour" "salt" to dough
fold "dark chocolate" into dough until uniform
chill dough in refrigerator for 720min to 4C
portion dough into balls weight 80g count 12
place dough on sheet spacing 50mm
tap sheet 2timescream "<ing>"... [in <vessel>] until <state>— beating fat with sugar so it
light, fluffy, pale, creamy,
aerated, ribbon. There is no duration clause.
add "<ing>"... to <vessel> [in <n>additions] [while mixing]— the preferred
apply. Use in <n>additions for "one at a time"; n is at least
2. Never write "gradually" — give the count.
fold <item>... into <vessel> [until <state>]— gentle incorporation.
combined, uniform, streak_free, marbled, folded.
portion <vessel> into <form> [weight <n>g] [count <n>] [size <n>mm]—
weight or count is required. weight
is the mass of each piece and takes g/kg; count takes a bare whole
number; size takes cm/mm.
chill <vessel> in <refrigerator> for <n>min [to <n>C]— refrigeration.
rest or wait for refrigeration.
place <item>... on <surface> [spacing <n>mm]— arranging on a sheet.tap <equipment> <n>times— rapping a sheet on the worktop, when the recipe
The target of add, fold, portion, chill and place is a **bare vessel
or stage name**, never a quoted mixture. If the recipe talks about "the dough"
or "the batter", declare a stage for it:
stage dough bowl "mixing bowl" 3lWaiting and resting
rest 5min
wait 4minUse rest and wait only for holding at room temperature. Anything that
says refrigerate, chill, or "overnight in the fridge" is chill. A rest or
wait over 120min produces a warning (W301).
Two rest/wait lines in a row is an error (E301) — combine them into one, or
put the action that happens between them on a line in between.
Conditions
until internal_temp >= 74CGoes on its own line, right after the statement it qualifies. Comparators:
>=, <=, >, <, ==.
Deglazing and reducing
deglaze pot with "red wine"
reduce 5min until au_secThe reduction target must be one of exactly six: au_sec, nappe,
thickened, concentrated, melded, glossy (E501).
Multiple vessels: stages
When a recipe needs two bowls, or a sauce built separately from the main
component, declare a stage — a named vessel you can refer to later:
stage zabaione bowl "mixing bowl" 3l
stage whipped_cream bowl "mixing bowl" 3l
apply "egg yolks" to zabaione
mix zabaione while heating to 65C until smooth
cool zabaione to 15C
combine zabaione whipped_cream in cream_mix
layer "ladyfingers" cream_mix "cocoa powder"stage <name> <equipment-type> "<equipment name>" [size]- Stage names are bare identifiers with underscores, not spaces.
combineneeds at least two sources, and each must be a declared stage.layertakes quoted ingredients and bare stage names, in order.
The older mechanical form
heat pan to 180C / apply "oil" to pan / wait 6min still parses, but
do not write new recipes this way. It describes the mechanics without naming
the technique, which is exactly what the technique statements above were added
to fix. Use them.
Food safety
These are not negotiable and there is no way to override them.
| Food | Minimum internal temperature |
|---|---|
| Poultry (chicken, turkey, duck) | 74 °C |
| Ground meat | 71 °C |
| Whole cuts and fish | 63 °C |
Any recipe declaring poultry must verify it:
ingredient "chicken breast" 600g
...
sear "chicken breast" in pan with "olive oil" at 180C for 6min
until internal_temp >= 74CWriting a lower number is a hard error (E101). Omitting the check entirely is a
warning (W101). If the source recipe implies undercooked poultry, **use 74 °C
anyway and tell the person you changed it.**
Before you hand it over
Check every one of these. Each maps to a rule that will reject the file.
- Exactly one
servingsline, first in the block. - Every ingredient the recipe uses is declared with
ingredient(E102). - Every declared ingredient is actually used somewhere (E106). Delete
- No ingredient is declared twice (E105). Combine the amounts instead.
- Every equipment reference is declared (E103), and every equipment name is
- A
knifeis declared if anycutappears (E401). - Ingredient names are double-quoted; equipment, stages, styles, states, and
- Every number has one of the ten units, with no space before it.
- Every temperature is Celsius. Every time is in
min(ors). - Poultry is verified at
>= 74C(E101 / W101). - No two
wait/restlines are adjacent (E301). reduce ... until Xuses one of the six valid targets (E501).- Every line in the block starts with a keyword, is blank, or starts with
#. tags:is a bare comma-separated list with no brackets or quotes.
The person can paste your file into
cookbook.edeny.dev/contribute/, which
runs the real validator and shows any remaining problem with its line number. If
they bring an error message back to you, fix it and return the whole file again.
Known ingredient names
Any ingredient name is accepted, but these are the ones the site has pages,
substitutions, and safety data for. Prefer them where they fit:
beef — "beef steak" (cook to 63C) · "ground beef" (cook to 71C)
dairy — "egg yolks" · "eggs"
fats — "butter" · "heavy cream" · "mascarpone" · "olive oil" · "vegetable oil"
fish — "salmon" (cook to 63C)
grains — "bread" · "breadcrumbs" · "chickpeas" · "flour" · "ladyfingers" · "pasta" · "rice" · "spaghetti"
liquids — "beef stock" · "beer" · "chicken stock" · "cream" · "espresso" · "lemon juice" · "marsala wine" · "red wine" · "tahini" · "vegetable stock" · "vinegar" · "water" · "white wine"
pork — "guanciale" (cook to 63C) · "pork chop" (cook to 63C)
poultry — "chicken breast" (cook to 74C) · "chicken thigh" (cook to 74C)
seasonings — "baking soda" · "black pepper" · "brown sugar" · "caraway seeds" · "chili flakes" · "cocoa powder" · "cumin" · "dark chocolate" · "Dijon mustard" · "lemon zest" · "marjoram" · "mozzarella" · "paprika" · "parmesan" · "salt" · "sugar" · "turmeric" · "vanilla extract"
vegetables — "basil" · "bell pepper" · "carrot" · "cauliflower" · "celery" · "cucumber" · "dill" · "garlic" · "mint" · "mushrooms" · "onion" · "parsley" · "potatoes" · "rosemary" · "shallots" · "thyme" · "tomato" · "zucchini"
A worked example
Input, as a person might paste it:
Grandma's garlic soup. Fry some bacon in a pot, add a few cloves of garlic and
cook till fragrant, pour in stock, simmer a while, season to taste. Serve with
croutons.
What you should ask:
I can turn this into a CKL recipe — a few things it doesn't pin down:
1. How many does it serve? (I'll assume 4.)
2. How much bacon and how much stock? (I'll assume 150 g bacon and
1 l stock.)
3. "A few cloves" of garlic — how many? (I'll assume 4, about 20 g.)
4. How long does it simmer? (I'll assume 20 min.)
5. "Season to taste" — roughly how much salt and pepper? (I'll assume 5 g
salt, 2 g black pepper.)
6. Are the croutons part of the recipe, or bought?
The result, once they answer:
<!--ckl-code-17-->ckl
servings 4
ingredient "bacon" 150g
ingredient "garlic" 20g
ingredient "chicken stock" 1l
ingredient "salt" 5g
ingredient "black pepper" 2g
pot "stock pot" 8l
knife "chef's knife"
cut "bacon" style dice size 10mm
cut "garlic" style mince
render "bacon" in pot at 150C for 6min
# Garlic burns fast once the fat is hot — keep it moving
bloom "garlic" in pot for 1min
simmer "chicken stock" in pot at 95C for 20min
season "chicken stock" with "salt" "black pepper"
rest 2min
<!--ckl-code-18-->
Notice what happened to the vague parts: every amount became a number, "cook
till fragrant" became for 1min with the visual cue moved to the notes, and
"season to taste" became explicit masses. Nothing was lost — it moved to where
it belongs.